Bundle autocomplete-css

This commit is contained in:
confused-Techie 2022-12-11 15:49:28 -08:00
parent 05b3976fc2
commit b7c94a875c
19 changed files with 4461 additions and 4 deletions

View File

@ -35,7 +35,7 @@
"atom-light-ui": "file:packages/atom-light-ui",
"atom-select-list": "^0.8.1",
"autocomplete-atom-api": "https://codeload.github.com/atom/autocomplete-atom-api/legacy.tar.gz/refs/tags/v0.10.7",
"autocomplete-css": "https://codeload.github.com/atom/autocomplete-css/legacy.tar.gz/refs/tags/v0.17.5",
"autocomplete-css": "file:packages/autocomplete-css",
"autocomplete-html": "https://github.com/pulsar-edit/autocomplete-html.git#v0.8.9",
"autocomplete-plus": "https://codeload.github.com/atom/autocomplete-plus/legacy.tar.gz/refs/tags/v2.42.4",
"autocomplete-snippets": "https://codeload.github.com/atom/autocomplete-snippets/legacy.tar.gz/refs/tags/v1.12.1",
@ -194,7 +194,7 @@
"about": "file:./packages/about",
"archive-view": "0.66.0",
"autocomplete-atom-api": "0.10.7",
"autocomplete-css": "0.17.5",
"autocomplete-css": "file:./packages/autocomplete-css",
"autocomplete-html": "0.8.9",
"autocomplete-plus": "2.42.4",
"autocomplete-snippets": "1.12.1",

View File

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

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

@ -0,0 +1 @@
node_modules

View File

@ -0,0 +1,40 @@
<!--
Have you read Atom's Code of Conduct? By filing an Issue, you are expected to comply with it, including treating everyone with respect: https://github.com/atom/atom/blob/master/CODE_OF_CONDUCT.md
Do you want to ask a question? Are you looking for support? The Atom message board is the best place for getting support: https://discuss.atom.io
-->
### Prerequisites
* [ ] Put an X between the brackets on this line if you have done all of the following:
* Reproduced the problem in Safe Mode: http://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode
* Followed all applicable steps in the debugging guide: http://flight-manual.atom.io/hacking-atom/sections/debugging/
* Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq
* Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom
* Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages
### Description
[Description of the issue]
### Steps to Reproduce
1. [First Step]
2. [Second Step]
3. [and so on...]
**Expected behavior:** [What you expect to happen]
**Actual behavior:** [What actually happens]
**Reproduces how often:** [What percentage of the time does it reproduce?]
### Versions
You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running.
### Additional Information
Any additional information, configuration or data that might be necessary to reproduce the issue.

View File

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

View File

@ -0,0 +1,28 @@
### Requirements
* Filling out the template is required. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion.
* All new code requires tests to ensure against regressions
### Description of the Change
<!--
We must be able to understand the design of your change from this description. If we can't get a good idea of what the code will be doing from the description here, the pull request may be closed at the maintainers' discretion. Keep in mind that the maintainer reviewing this PR may not be familiar with or have worked with the code here recently, so please walk us through the concepts.
-->
### Alternate Designs
<!-- Explain what other alternates were considered and why the proposed version was selected -->
### Benefits
<!-- What benefits will be realized by the code change? -->
### Possible Drawbacks
<!-- What are the possible side-effects or negative impacts of the code change? -->
### Applicable Issues
<!-- Enter any applicable Issues here -->

View File

@ -0,0 +1,13 @@
# CSS Autocomplete package
[![OS X Build Status](https://travis-ci.org/atom/autocomplete-css.svg?branch=master)](https://travis-ci.org/atom/autocomplete-css) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k3e5uvpmpc5bkja9/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/autocomplete-css/branch/master) [![Dependency Status](https://david-dm.org/atom/autocomplete-css.svg)](https://david-dm.org/atom/autocomplete-css)
CSS property name and value autocompletions in Atom. Uses the
[autocomplete-plus](https://github.com/atom-community/autocomplete-plus) package.
This is powered by the list of CSS property and values [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/CSSCodeHints/CSSProperties.json).
![css-completions](https://cloud.githubusercontent.com/assets/671378/6357910/b9ecbe7c-bc1c-11e4-89b1-033e626c891f.gif)
You can update the prebuilt list of property names and values by running the `update.coffee` file at the root of the repository and then checking in the changed `properties.json` file.
`sorted-property-names.json` is updated manually - take a look at https://developer.microsoft.com/en-us/microsoft-edge/platform/usage/ and https://www.chromestatus.com/metrics/css/popularity for guidance.

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2270,9 +2270,8 @@ atom-slick@^2, atom-slick@^2.0.0:
version "0.10.7"
resolved "https://codeload.github.com/atom/autocomplete-atom-api/legacy.tar.gz/refs/tags/v0.10.7#c9d51fa721d543ccfc1b2189101155e81db6b97d"
"autocomplete-css@https://codeload.github.com/atom/autocomplete-css/legacy.tar.gz/refs/tags/v0.17.5":
"autocomplete-css@file:packages/autocomplete-css":
version "0.17.5"
resolved "https://codeload.github.com/atom/autocomplete-css/legacy.tar.gz/refs/tags/v0.17.5#a6b38bca90bdba18a2f40c366266cda93bf582c3"
"autocomplete-html@https://github.com/pulsar-edit/autocomplete-html.git#v0.8.9":
version "0.8.9"