-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(auto-complete): experimental auto complete from view model (behi…
…nd feature toggle: smartAutocomplete) experimental feature: smart auto complete (behind feature toggle "smartAutocomplete") Basic implementation to enable autocomplete for items from the view model. This is a very limited/ simple version, currently, it only works on bindings and hints simple properties, doesn't work on string interpolation bindings.
- Loading branch information
Erik Lieben
authored
Jan 1, 2018
1 parent
ea625e1
commit 7312b03
Showing
24 changed files
with
986 additions
and
320 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import * as vscode from 'vscode'; | ||
import {TextDocumentContentProvider} from './TextDocumentContentProvider'; | ||
|
||
export function registerPreview(context, window, client) { | ||
|
||
let previewUri = vscode.Uri.parse('aurelia-preview://authority/aurelia-preview'); | ||
|
||
let provider = new TextDocumentContentProvider(client); | ||
let registration = vscode.workspace.registerTextDocumentContentProvider('aurelia-preview', provider); | ||
|
||
vscode.workspace.onDidChangeTextDocument((e: vscode.TextDocumentChangeEvent) => { | ||
if (e.document === vscode.window.activeTextEditor.document) { | ||
provider.update(previewUri); | ||
} | ||
}); | ||
|
||
vscode.window.onDidChangeTextEditorSelection((e: vscode.TextEditorSelectionChangeEvent) => { | ||
if (e.textEditor === vscode.window.activeTextEditor) { | ||
provider.update(previewUri); | ||
} | ||
}); | ||
|
||
context.subscriptions.push(vscode.commands.registerCommand('aurelia.showViewProperties', () => { | ||
|
||
const smartAutocomplete = vscode.workspace.getConfiguration().get('aurelia.featureToggles.smartAutocomplete'); | ||
if (smartAutocomplete) { | ||
return vscode.commands.executeCommand('vscode.previewHtml', previewUri, vscode.ViewColumn.Two, 'Aurelia view data') | ||
.then( | ||
(success) => { | ||
}, | ||
(reason) => { | ||
window.showErrorMessage(reason); | ||
}); | ||
} else { | ||
return vscode.window.showWarningMessage('This command requires the experimental feature "smartAutocomplete" to be enabled'); | ||
} | ||
|
||
|
||
})); | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import * as vscode from 'vscode'; | ||
import { LanguageClient } from 'vscode-languageclient'; | ||
import { WebComponent } from './../../server/FileParser/Model/WebComponent'; | ||
|
||
export class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { | ||
|
||
constructor(private client: LanguageClient) { | ||
|
||
} | ||
|
||
private _onDidChange = new vscode.EventEmitter<vscode.Uri>(); | ||
|
||
public async provideTextDocumentContent(uri: vscode.Uri): Promise<string> { | ||
let editor = vscode.window.activeTextEditor; | ||
if (!vscode.window.activeTextEditor.document) { | ||
return Promise.resolve('<p>no data</p>'); | ||
} | ||
let fileName = vscode.window.activeTextEditor.document.fileName; | ||
let component = <WebComponent> await this.client.sendRequest('aurelia-view-information', fileName); | ||
|
||
let headerHTML = `<h1>Component: '${component.name}'</h1>`; | ||
headerHTML += '<h2>Files</h2><ul>'; | ||
for (let path of component.paths) { | ||
headerHTML += `<li>${path}</li>`; | ||
} | ||
headerHTML += '</ul>'; | ||
|
||
let viewModelHTML = `<h2>no viewmodel found</h2>`; | ||
if (component.viewModel) { | ||
viewModelHTML = `<h2>ViewModel</h2>`; | ||
viewModelHTML += `<p>type: ${component.viewModel.type}</p>`; | ||
viewModelHTML += `<h3>Properties</h3>`; | ||
viewModelHTML += '<ul>'; | ||
for (let prop of component.viewModel.properties) { | ||
viewModelHTML += `<li>${prop.name} (${prop.type})</li>`; | ||
} | ||
viewModelHTML += '</ul>'; | ||
viewModelHTML += `<h3>Methods</h3>`; | ||
viewModelHTML += '<ul>'; | ||
for (let prop of component.viewModel.methods) { | ||
viewModelHTML += `<li>${prop.name} (${prop.returnType}) => (params: ${prop.parameters.join(',')})</li>`; | ||
} | ||
viewModelHTML += '</ul>'; | ||
} | ||
|
||
let viewHTML = `<h2>No view found</h2>`; | ||
if (component.document) { | ||
viewHTML = `<h2>View</h2>`; | ||
|
||
|
||
if (component.document.references && component.document.references.length) { | ||
viewHTML += '<h3>Require/ references in template</h3>'; | ||
viewHTML += `<ul>`; | ||
for(let reference of component.document.references) { | ||
viewHTML += `<li>path: ${reference.path}</li>`; | ||
viewHTML += `<li>as: ${reference.as}</li>`; | ||
} | ||
viewHTML += '</ul>'; | ||
} | ||
|
||
if (component.document.bindables && component.document.bindables.length) { | ||
viewHTML += '<h3>bindable property on template</h3><ul>'; | ||
for (let bindable of component.document.bindables) { | ||
viewHTML += `<li>${bindable}</li>`; | ||
} | ||
viewHTML += '</ul>'; | ||
} | ||
|
||
if (component.document.dynamicBindables && component.document.dynamicBindables.length) { | ||
viewHTML += '<h3>Attributes with bindings found in template</h3>'; | ||
for(let bindable of component.document.dynamicBindables) { | ||
viewHTML += ` | ||
<ul> | ||
<li>attribute name : <code>${bindable.name}</code></li> | ||
<li>attribute value : <code>${bindable.value}</code></li> | ||
<li>binding type: <code>${bindable.bindingType}</code></li> | ||
</ul>`; | ||
viewHTML += `<pre><code>${JSON.stringify(bindable.bindingData, null, 2) }</code></pre>`; | ||
} | ||
} | ||
|
||
if (component.document.interpolationBindings && component.document.interpolationBindings.length) { | ||
viewHTML += '<h2>String interpolation found in template</h2>'; | ||
for(let bindable of component.document.interpolationBindings) { | ||
viewHTML += ` | ||
<code>${bindable.value}</code>`; | ||
viewHTML += `<pre><code>${JSON.stringify(bindable.bindingData, null, 2) }</code></pre>`; | ||
} | ||
} | ||
} | ||
|
||
let classesHTML = '<h2>no extra classes found</h2>'; | ||
if (component.classes) { | ||
classesHTML = '<h2>exports to this view</h2>'; | ||
for(let cl of component.classes) { | ||
classesHTML += `<li>${cl.name}</li>`; | ||
} | ||
classesHTML += '</ul>'; | ||
|
||
} | ||
|
||
return `<body><style>pre { border: 1px solid #333; display: block; background: #1a1a1a; margin: 1rem;color: #999; }</style> | ||
${headerHTML} | ||
<hr> | ||
${viewModelHTML} | ||
<hr> | ||
${viewHTML} | ||
<hr> | ||
${classesHTML} | ||
<br><br> | ||
</body>`; | ||
} | ||
|
||
get onDidChange(): vscode.Event<vscode.Uri> { | ||
return this._onDidChange.event; | ||
} | ||
|
||
public update(uri: vscode.Uri) { | ||
this._onDidChange.fire(uri); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 97 additions & 42 deletions
139
src/server/Completions/AttributeValueCompletionFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,97 @@ | ||
import { | ||
CompletionItem, | ||
CompletionItemKind, | ||
InsertTextFormat } from 'vscode-languageserver-types'; | ||
import { autoinject } from 'aurelia-dependency-injection'; | ||
import ElementLibrary from './Library/_elementLibrary'; | ||
import { GlobalAttributes } from './Library/_elementStructure'; | ||
import BaseAttributeCompletionFactory from './BaseAttributeCompletionFactory'; | ||
|
||
@autoinject() | ||
export default class AttributeCompletionFactory extends BaseAttributeCompletionFactory { | ||
|
||
constructor(library: ElementLibrary) { super(library); } | ||
|
||
public create(elementName: string, attributeName: string, bindingName: string): Array<CompletionItem> { | ||
|
||
let result:Array<CompletionItem> = []; | ||
|
||
if (bindingName === undefined || bindingName === null || bindingName === '') { | ||
let element = this.getElement(elementName); | ||
|
||
let attribute = element.attributes.get(attributeName); | ||
if (!attribute) { | ||
attribute = GlobalAttributes.attributes.get(attributeName); | ||
} | ||
|
||
if (attribute && attribute.values) { | ||
for (let [key, value] of attribute.values.entries()) { | ||
result.push({ | ||
documentation: value.documentation, | ||
insertText: key, | ||
insertTextFormat: InsertTextFormat.Snippet, | ||
kind: CompletionItemKind.Property, | ||
label: key, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
} | ||
import { | ||
CompletionItem, | ||
CompletionItemKind, | ||
InsertTextFormat } from 'vscode-languageserver-types'; | ||
import { autoinject } from 'aurelia-dependency-injection'; | ||
import ElementLibrary from './Library/_elementLibrary'; | ||
import { GlobalAttributes } from './Library/_elementStructure'; | ||
import BaseAttributeCompletionFactory from './BaseAttributeCompletionFactory'; | ||
import {AureliaApplication} from './../FileParser/Model/AureliaApplication'; | ||
import AureliaSettings from '../AureliaSettings'; | ||
import { settings } from 'cluster'; | ||
import { fileUriToPath } from './../Util/FileUriToPath'; | ||
import { normalizePath } from './../Util/NormalizePath'; | ||
|
||
@autoinject() | ||
export default class AttributeCompletionFactory extends BaseAttributeCompletionFactory { | ||
|
||
constructor( | ||
library: ElementLibrary, | ||
private application: AureliaApplication, | ||
private settings: AureliaSettings) { super(library); } | ||
|
||
public create(elementName: string, attributeName: string, bindingName: string, uri: string): Array<CompletionItem> { | ||
|
||
let result:Array<CompletionItem> = []; | ||
|
||
if (bindingName === undefined || bindingName === null || bindingName === '') { | ||
let element = this.getElement(elementName); | ||
|
||
let attribute = element.attributes.get(attributeName); | ||
if (!attribute) { | ||
attribute = GlobalAttributes.attributes.get(attributeName); | ||
} | ||
|
||
if (attribute && attribute.values) { | ||
for (let [key, value] of attribute.values.entries()) { | ||
result.push({ | ||
documentation: value.documentation, | ||
insertText: key, | ||
insertTextFormat: InsertTextFormat.Snippet, | ||
kind: CompletionItemKind.Property, | ||
label: key, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
if (this.settings.featureToggles.smartAutocomplete) { | ||
includeCodeAutoComplete(this.application, result, normalizePath(fileUriToPath(uri))); | ||
} | ||
|
||
return result; | ||
} | ||
} | ||
|
||
function includeCodeAutoComplete(application, result, path) { | ||
path = path.toLowerCase(); | ||
const compoment = application.components.find(i => i.paths.map(x => x.toLowerCase()).indexOf(path) > -1); | ||
|
||
if (compoment) { | ||
if (compoment.viewModel) { | ||
compoment.viewModel.methods.forEach(x => { | ||
|
||
let inner = ''; | ||
for(let i=0; i < x.parameters.length;i++) { | ||
inner += `\$${i+1},`; | ||
} | ||
if (x.parameters.length) { | ||
inner = inner.substring(0, inner.length-1); | ||
} | ||
|
||
result.push({ | ||
documentation: x.name, | ||
insertText: `${x.name}(${inner})$0`, | ||
insertTextFormat: InsertTextFormat.Snippet, | ||
kind: CompletionItemKind.Method, | ||
label: x.name, | ||
}); | ||
}); | ||
|
||
compoment.viewModel.properties.forEach(x => { | ||
let documentation = x.name; | ||
if (x.type) { | ||
documentation += ` (${x.type})`; | ||
} | ||
|
||
result.push({ | ||
documentation: documentation, | ||
insertText: x.name, | ||
insertTextFormat: InsertTextFormat.Snippet, | ||
kind: CompletionItemKind.Property, | ||
label: x.name, | ||
}) | ||
}); | ||
} | ||
} | ||
} |
Oops, something went wrong.