Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Add HTML Entities Autocomplete #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ HTML tag and attribe autocompletions in Atom. Install
[autocomplete-plus](https://github.com/atom-community/autocomplete-plus) before
installing this package.

This is powered by the list of HTML tags [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlTags.json)
and HTML attributes [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlAttributes.json)
This is powered by the list of HTML tags [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlTags.json), HTML attributes [here](https://github.com/adobe/brackets/blob/master/src/extensions/default/HTMLCodeHints/HtmlAttributes.json) and Special Chars [here](https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HtmlEntityCodeHints/SpecialChars.json)

![html-completions](https://cloud.githubusercontent.com/assets/4392286/7382905/705e6174-ee59-11e4-88bf-40bd553a336c.gif)

Expand Down
2 changes: 1 addition & 1 deletion completions.json

Large diffs are not rendered by default.

42 changes: 41 additions & 1 deletion lib/provider.coffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fs = require 'fs'
path = require 'path'
he = require 'he'

trailingWhitespace = /\s$/
attributePattern = /\s+([a-zA-Z][-a-zA-Z]*)\s*=\s*$/
Expand All @@ -12,7 +13,11 @@ module.exports =

getSuggestions: (request) ->
{prefix} = request
if @isAttributeValueStartWithNoPrefix(request)
if @isEntityStartWithNoPrefix(request)
@getEntityNameCompletions()
else if @isEntityStartWithPrefix(request)
@getEntityNameCompletions(prefix)
else if @isAttributeValueStartWithNoPrefix(request)
@getAttributeValueCompletions(request)
else if @isAttributeValueStartWithPrefix(request)
@getAttributeValueCompletions(request, prefix)
Expand Down Expand Up @@ -74,6 +79,22 @@ module.exports =
scopes = scopeDescriptor.getScopesArray()
@hasStringScope(scopes) and @hasTagScope(scopes)


isEntityStartWithNoPrefix: ({scopeDescriptor, prefix}) ->
scopes = scopeDescriptor.getScopesArray()
if prefix is '&' and scopes.length is 1
scopes[0] is 'text.html.basic'
else if prefix is '&' and scopes.length is 2
scopes[0] is 'text.html.basic' and scopes[1] is 'invalid.illegal.bad-ampersand.html'
else
false

isEntityStartWithPrefix: ({scopeDescriptor, prefix}) ->
return false unless prefix
return false if trailingWhitespace.test(prefix)

@hasEntityScope(scopeDescriptor.getScopesArray())

hasTagScope: (scopes) ->
scopes.indexOf('meta.tag.any.html') isnt -1 or
scopes.indexOf('meta.tag.other.html') isnt -1 or
Expand All @@ -85,18 +106,34 @@ module.exports =
scopes.indexOf('string.quoted.double.html') isnt -1 or
scopes.indexOf('string.quoted.single.html') isnt -1

hasEntityScope: (scopes) ->
scopes.indexOf('constant.character.entity.html') isnt -1 or
scopes.indexOf('entity.name.entity.other.html') isnt -1

getTagNameCompletions: (prefix) ->
completions = []
for tag, attributes of @completions.tags when not prefix or firstCharsEqual(tag, prefix)
completions.push(@buildTagCompletion(tag))
completions

getEntityNameCompletions: (prefix) ->
completions = []
for entity in @completions.entities when not prefix or firstCharsEqual(entity, prefix)
completions.push(@buildEntityCompletion(entity))
completions

buildTagCompletion: (tag) ->
text: tag
type: 'tag'
description: "HTML <#{tag}> tag"
descriptionMoreURL: @getTagDocsURL(tag)

buildEntityCompletion: (entity) ->
text: entity
type: 'entity'
description: he.decode("&#{entity}")
descriptionMoreURL: @getEntityDocsURL(entity)

getAttributeNameCompletions: ({editor, bufferPosition}, prefix) ->
completions = []
tag = @getPreviousTag(editor, bufferPosition)
Expand Down Expand Up @@ -184,5 +221,8 @@ module.exports =
getGlobalAttributeDocsURL: (attribute) ->
"https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/#{attribute}"

getEntityDocsURL: (entity) ->
"https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML"

firstCharsEqual = (str1, str2) ->
str1[0].toLowerCase() is str2[0].toLowerCase()
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
"devDependencies": {
"coffeelint": "^1.9.7",
"request": "^2.53.0"
},
"dependencies": {
"he": "^0.5.0"
}
}
36 changes: 36 additions & 0 deletions spec/provider-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,39 @@ describe "HTML autocompletions", ->
args = atom.commands.dispatch.mostRecentCall.args
expect(args[0].tagName.toLowerCase()).toBe 'atom-text-editor'
expect(args[1]).toBe 'autocomplete-plus:activate'

it "autocompletes entity without a prefix", ->
editor.setText('&')
editor.setCursorBufferPosition([0, 1])

completions = getCompletions()
expect(completions.length).toBe 276
expect(completions[0].descriptionMoreURL.endsWith('/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML')).toBe true

expect(completions[0].text).toBe '#33;'
expect(completions[1].text).toBe '#35;'
expect(completions[2].text).toBe '#36;'
expect(completions[3].text).toBe '#37;'
expect(completions[4].text).toBe '#39;'
expect(completions[5].text).toBe '#40;'

for completion in completions
expect(completion.type).toBe 'entity'

it "autocompletes entity with a prefix", ->
editor.setText('&a')
editor.setCursorBufferPosition([0, 2])

completions = getCompletions()
expect(completions.length).toBe 22
expect(completions[0].descriptionMoreURL.endsWith('/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML')).toBe true

expect(completions[0].text).toBe 'aacute;'
expect(completions[1].text).toBe 'Aacute;'
expect(completions[2].text).toBe 'acirc;'
expect(completions[3].text).toBe 'Acirc;'
expect(completions[4].text).toBe 'acute;'
expect(completions[5].text).toBe 'aelig;'

for completion in completions
expect(completion.type).toBe 'entity'
22 changes: 20 additions & 2 deletions update.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,29 @@ getAttributes = (callback) ->

callback(null, attributes)

getEntities = (callback) ->
requestOptions =
url: 'https://raw.githubusercontent.com/adobe/brackets/master/src/extensions/default/HtmlEntityCodeHints/SpecialChars.json'
json: true

request requestOptions, (error, response, entities) ->
return callback(error) if error?

if response.statusCode isnt 200
return callback(new Error("Request for SpecialChars.json failed: #{response.statusCode}"))

entities = ((entity.substring 1) + ';' for entity in entities)

callback(null, entities)

getTags (error, tags) ->
exitIfError(error)

getAttributes (error, attributes) ->
exitIfError(error)

completions = {tags, attributes}
fs.writeFileSync(path.join(__dirname, 'completions.json'), "#{JSON.stringify(completions, null, 0)}\n")
getEntities (error, entities) ->
exitIfError(error)

completions = {tags, attributes, entities}
fs.writeFileSync(path.join(__dirname, 'completions.json'), "#{JSON.stringify(completions, null, 0)}\n")