Skip to content

Commit

Permalink
feat(wxml): auto completion for close tag (#41)
Browse files Browse the repository at this point in the history
feat(wxml): auto completion for close tag
  • Loading branch information
qiu8310 authored Jun 24, 2019
2 parents a8aa63b + 919f8fd commit eba2ba0
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function activate(context: ExtensionContext) {
languages.registerDefinitionProvider([pug].concat(wxml), propDefinitionProvider),

// 自动补全
languages.registerCompletionItemProvider(wxml, autoCompletionWxml, '<', ' ', ':', '@', '.', '-', '"', '\'', '\n'),
languages.registerCompletionItemProvider(wxml, autoCompletionWxml, '<', ' ', ':', '@', '.', '-', '"', '\'', '\n', '/'),
languages.registerCompletionItemProvider(pug, autoCompletionPug, '\n', ' ', '(', ':', '@', '.', '-', '"', '\''),
// trigger 需要是上两者的和
languages.registerCompletionItemProvider(vue, autoCompletionVue, '<', ' ', ':', '@', '.', '-', '(', '"', '\'')
Expand Down
49 changes: 39 additions & 10 deletions src/plugin/AutoCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import {

import * as path from 'path'

import {Config} from './lib/config'
import {getCustomOptions, getTextAtPosition, getRoot, getEOL} from './lib/helper'
import {LanguageConfig} from './lib/language'
import {getTagAtPosition} from './getTagAtPosition/'
import { Config } from './lib/config'
import { getCustomOptions, getTextAtPosition, getRoot, getEOL, getLastChar } from './lib/helper'
import { LanguageConfig } from './lib/language'
import { getTagAtPosition } from './getTagAtPosition/'
import * as s from './res/snippets'
import { getClass } from './lib/StyleFile'
import { getCloseTag } from './lib/closeTag'

export default abstract class AutoCompletion {
abstract id: 'wxml' | 'wxml-pug'
Expand All @@ -32,7 +33,7 @@ export default abstract class AutoCompletion {
return this.isPug ? this.config.pugQuoteStyle : this.config.wxmlQuoteStyle
}

constructor(public config: Config) {}
constructor(public config: Config) { }

getCustomOptions(doc: TextDocument) {
return getCustomOptions(this.config, doc)
Expand All @@ -42,7 +43,7 @@ export default abstract class AutoCompletion {
let c = tag.component
let item = new CompletionItem(c.name, CompletionItemKind.Module)

let {attrQuote, isPug} = this
let { attrQuote, isPug } = this
let allAttrs = c.attrs || []
let attrs = allAttrs
.filter(a => a.required || a.subAttrs)
Expand Down Expand Up @@ -80,7 +81,7 @@ export default abstract class AutoCompletion {
defaultValue = a.enum && a.enum[0].value
}

let {attrQuote, isPug} = this
let { attrQuote, isPug } = this

if (a.boolean) {
item.insertText = new SnippetString(isPug && defaultValue === 'false' ? `${a.name}=false` : a.name)
Expand All @@ -90,7 +91,7 @@ export default abstract class AutoCompletion {
: this.setDefault(1, defaultValue)

// 是否有可选值,如果有可选值则触发命令的自动补全
let values = a.enum ? a.enum : a.subAttrs ? a.subAttrs.map(sa => ({value: sa.equal})) : []
let values = a.enum ? a.enum : a.subAttrs ? a.subAttrs.map(sa => ({ value: sa.equal })) : []
if (values.length) {
value = '\${1}'
item.command = autoSuggestCommand()
Expand Down Expand Up @@ -149,7 +150,7 @@ export default abstract class AutoCompletion {

// 添加 Snippet
let userSnippets = this.config.snippets
let allSnippets: s.Snippets = (this.isPug ? {...s.PugSnippets, ...userSnippets.pug} : {...s.WxmlSnippets, ...userSnippets.wxml})
let allSnippets: s.Snippets = (this.isPug ? { ...s.PugSnippets, ...userSnippets.pug } : { ...s.WxmlSnippets, ...userSnippets.wxml })
items.push(...Object.keys(allSnippets)
.filter(k => filter(k))
.map(k => {
Expand Down Expand Up @@ -207,7 +208,7 @@ export default abstract class AutoCompletion {
let res = await autocompleteTagAttr(tag.name, tag.attrs, lc, this.getCustomOptions(doc))
let triggers: CompletionItem[] = []

let {natives, basics} = res
let { natives, basics } = res
let noBasics = lc.noBasicAttrsComponents || []

if (noBasics.indexOf(tag.name) < 0) {
Expand Down Expand Up @@ -299,6 +300,34 @@ export default abstract class AutoCompletion {

return items
}

/**
* 闭合标签自动完成
* @param doc
* @param pos
*/
async createCloseTagCompletionItem(doc: TextDocument, pos: Position): Promise<CompletionItem[]> {
const text = doc.getText(new Range(new Position(0, 0), pos))
if (text.length < 2 || text.substr(text.length - 2) !== '</') {
return []
}
const closeTag = getCloseTag(text)
if (closeTag) {
const completionItem = new CompletionItem(closeTag)
completionItem.kind = CompletionItemKind.Property
completionItem.insertText = closeTag

const nextPos = new Position(pos.line, pos.character + 1)
if (getLastChar(doc, nextPos) === '>') {
completionItem.range = new Range(pos, nextPos)
completionItem.label = closeTag.substr(0, closeTag.length - 1)
}
return [completionItem]
}

return []
}

}

function autoSuggestCommand() {
Expand Down
5 changes: 5 additions & 0 deletions src/plugin/WxmlAutoCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export default class extends AutoCompletion implements CompletionItemProvider {
id = 'wxml' as 'wxml'

provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionItem[]> {
if (token.isCancellationRequested) {
return Promise.resolve([])
}
let language = getLanguage(document, position)
if (!language) return [] as any

Expand All @@ -39,6 +42,8 @@ export default class extends AutoCompletion implements CompletionItemProvider {
case '-': // v-if
case '.': // 变量或事件的修饰符
return this.createSpecialAttributeSnippetItems(language, document, position)
case '/': // 闭合标签
return this.createCloseTagCompletionItem(document, position)
default:
if (char >= 'a' && char <= 'z') {
// 输入属性时自动提示
Expand Down
46 changes: 46 additions & 0 deletions src/plugin/lib/closeTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 获取闭合标签
* from: https://github.com/formulahendry/vscode-auto-close-tag/blob/master/src/extension.ts
* @param text
* @param excludedTags
*/
export function getCloseTag(text: string, excludedTags: string[] = []): string {
// 过滤变量/和值中`<``>`
text = text.replace(/"[^"]*"|'[^']*'|\{\{[^\}]*?\}\}/g, '')
// const regex = /<(\/?[\w\d-]*)(?:\s+[^<>]*?[^\s/<>=]+?)*?\s?>/g
const regex = /<(\/?[\w\d-]+)[^<>]*\s?>/g // 简化正则提高性能

let result = null
const stack = []
// tslint:disable-next-line: no-conditional-assignment
while (result = regex.exec(text)) {
if (!result[1] || result[0].endsWith('/>')) {
// 自闭标签
continue
}
const isStartTag = result[1].substr(0, 1) !== '/'
const tag = isStartTag ? result[1] : result[1].substr(1)
if (excludedTags.indexOf(tag.toLowerCase()) === -1) {
if (isStartTag) {
stack.push(tag)
} else if (stack.length > 0) {
const lastTag = stack[stack.length - 1]
if (lastTag === tag) {
stack.pop()
}
}
}
}
if (stack.length > 0) {
const closeTag = stack[stack.length - 1]
if (text.substr(text.length - 2) === '</') {
return closeTag + '>'
}
if (text.substr(text.length - 1) === '<') {
return '/' + closeTag + '>'
}
return '</' + closeTag + '>'
} else {
return ''
}
}

0 comments on commit eba2ba0

Please sign in to comment.