This commit is contained in:
Paulo Aboim Pinto 2022-04-07 23:28:33 +02:00
Коммит aaefbeae31
18 изменённых файлов: 6607 добавлений и 0 удалений

24
.eslintrc.json Normal file
Просмотреть файл

@ -0,0 +1,24 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
]
}

5
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
out
dist
node_modules
.vscode-test/
*.vsix

5
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher"]
}

35
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,35 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js",
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "tasks: watch-tests"
}
]
}

13
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false, // set this to true to hide the "out" folder with the compiled JS files
"dist": false // set this to true to hide the "dist" folder with the compiled JS files
},
"search.exclude": {
"out": true, // set this to false to include "out" folder in search results
"dist": true // set this to false to include "dist" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
}

43
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,43 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": [
"$ts-webpack-watch",
"$tslint-webpack-watch"
],
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "watch-tests",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never",
"group": "watchers"
},
"group": "build"
},
{
"label": "tasks: watch-tests",
"dependsOn": [
"npm: watch",
"npm: watch-tests"
],
"problemMatcher": []
}
]
}

13
.vscodeignore Normal file
Просмотреть файл

@ -0,0 +1,13 @@
.vscode/**
.vscode-test/**
out/**
node_modules/**
src/**
.gitignore
.yarnrc
webpack.config.js
vsc-extension-quickstart.md
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts

9
CHANGELOG.md Normal file
Просмотреть файл

@ -0,0 +1,9 @@
# Change Log
All notable changes to the "avalonia-tagsandidentation" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

70
README.md Normal file
Просмотреть файл

@ -0,0 +1,70 @@
# avalonia-tagsandidentation README
This is the README for your extension "avalonia-tagsandidentation". After writing up a brief description, we recommend including the following sections.
## Features
Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file.
For example if there is an image subfolder under your extension project workspace:
\!\[feature X\]\(images/feature-x.png\)
> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow.
## Requirements
If you have any requirements or dependencies, add a section describing those and how to install and configure them.
## Extension Settings
Include if your extension adds any VS Code settings through the `contributes.configuration` extension point.
For example:
This extension contributes the following settings:
* `myExtension.enable`: enable/disable this extension
* `myExtension.thing`: set to `blah` to do something
## Known Issues
Calling out known issues can help limit users opening duplicate issues against your extension.
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of ...
### 1.0.1
Fixed issue #.
### 1.1.0
Added features X, Y, and Z.
-----------------------------------------------------------------------------------------------------------
## Following extension guidelines
Ensure that you've read through the extensions guidelines and follow the best practices for creating your extension.
* [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
## Working with Markdown
**Note:** You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts:
* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux)
* Toggle preview (`Shift+CMD+V` on macOS or `Shift+Ctrl+V` on Windows and Linux)
* Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (macOS) to see a list of Markdown snippets
### For more information
* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown)
* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/)
**Enjoy!**

6026
package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

63
package.json Normal file
Просмотреть файл

@ -0,0 +1,63 @@
{
"name": "avalonia-tagsandidentation",
"displayName": "Avalonia.TagsAndIdentation",
"description": "Responsable for Auto-Close aXAML tags and indent the right way",
"version": "0.0.1",
"engines": {
"vscode": "^1.65.0"
},
"categories": [
"Programming Languages",
"Linters"
],
"keywords": [
"xml",
"xaml",
"avalonia",
"wpf",
"xsd",
"completion",
"linter",
"lint",
"format"
],
"activationEvents": [
"onLanguage:xml"
],
"main": "./dist/extension.js",
"contributes": {
"languages": [
{
"id": "xml",
"configuration": "./src/language-configuration.json"
}
]
},
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "webpack",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"compile-tests": "tsc -p . --outDir out",
"watch-tests": "tsc -p . -w --outDir out",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.65.0",
"@types/glob": "^7.1.4",
"@types/mocha": "^9.0.0",
"@types/node": "14.x",
"@typescript-eslint/eslint-plugin": "^5.1.0",
"@typescript-eslint/parser": "^5.1.0",
"eslint": "^8.1.0",
"glob": "^7.1.7",
"mocha": "^9.1.3",
"typescript": "^4.4.4",
"ts-loader": "^9.2.5",
"webpack": "^5.52.1",
"webpack-cli": "^4.8.0",
"@vscode/test-electron": "^1.6.2"
}
}

152
src/extension.ts Normal file
Просмотреть файл

@ -0,0 +1,152 @@
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidChangeTextDocument(event => {
insertAutoCloseTag(event);
});
}
export function deactivate() {}
function insertAutoCloseTag(event: vscode.TextDocumentChangeEvent): void {
if (!event.contentChanges[0]) {
return;
}
let isRightAngleBracket = checkRightAngleBracket(event.contentChanges[0]);
if (!isRightAngleBracket && event.contentChanges[0].text !== "/") {
return;
}
let editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
let selection = editor.selection;
let originalPosition = selection.start.translate(0, 1);
let excludedTags: string[] = [];
let textLine = editor.document.lineAt(selection.start);
let text = textLine.text.substring(0, selection.start.character + 1);
let result = /<([_a-zA-Z][a-zA-Z0-9:\-_.]*)(?:\s+[^<>]*?[^\s/<>=]+?)*?\s?(\/|>)$/.exec(text);
let selectedLine = selection.start.line;
let multilineText = "";
while(result === null) {
selectedLine --;
multilineText = editor.document.getText(
new vscode.Range(
new vscode.Position(selectedLine, 0),
new vscode.Position(selection.start.line, selection.start.character + 1)));
multilineText = multilineText.trim();
multilineText = multilineText.replace("\t", "");
multilineText = multilineText.replace("\r\n", "");
result = /<([_a-zA-Z][a-zA-Z0-9:\-_.]*)(?:\s+[^<>]*?[^\s/<>=]+?)*?\s?(\/|>)$/.exec(multilineText);
}
if (
result !== null &&
((occurrenceCount(result[0], "'") % 2 === 0) &&
(occurrenceCount(result[0], "\"") % 2 === 0) &&
(occurrenceCount(result[0], "`") % 2 === 0))) {
if (result[2] === ">") {
if (excludedTags.indexOf(result[1].toLowerCase()) === -1) {
editor.edit((editBuilder) => {
if (result !== null) {
editBuilder.insert(originalPosition, "</" + result[1] + ">");
}
}).then(() => {
if (editor !== undefined) {
editor.selection = new vscode.Selection(originalPosition, originalPosition);
}
});
}
}
else {
if (textLine.text.length <= selection.start.character + 1 || textLine.text[selection.start.character + 1] !== '>') { // if not typing "/" just before ">", add the ">" after "/"
if (textLine.text[selection.start.character - 1] !== " ")
{
editor.edit((editBuilder) => {
const spacePosition = originalPosition.translate(0, -1);
editBuilder.insert(spacePosition, " ");
editBuilder.insert(originalPosition, ">");
});
}
else
{
editor.edit((editBuilder) => {
const spacePosition = originalPosition.translate(0, -1);
editBuilder.insert(originalPosition, ">");
});
}
}
}
}
}
function checkRightAngleBracket(contentChange: vscode.TextDocumentContentChangeEvent): boolean {
return contentChange.text === ">" || checkRightAngleBracketInVSCode_1_8(contentChange);
}
function checkRightAngleBracketInVSCode_1_8(contentChange: vscode.TextDocumentContentChangeEvent): boolean {
return contentChange.text.endsWith(">") && contentChange.range.start.character === 0
&& contentChange.range.start.line === contentChange.range.end.line
&& !contentChange.range.end.isEqual(new vscode.Position(0, 0));
}
function getNextChar(editor: vscode.TextEditor, position: vscode.Position): string {
let nextPosition = position.translate(0, 1);
let text = editor.document.getText(new vscode.Range(position, nextPosition));
return text;
}
function getCloseTag(text: string, excludedTags: string[]): string {
let regex = /<(\/?[_a-zA-Z][a-zA-Z0-9:\-_.]*)(?:\s+[^<>]*?[^\s/<>=]+?)*?\s?>/g;
let result = null;
let stack = [];
while ((result = regex.exec(text)) !== null) {
let isStartTag = result[1].substr(0, 1) !== "/";
let tag = isStartTag ? result[1] : result[1].substr(1);
if (excludedTags.indexOf(tag.toLowerCase()) === -1) {
if (isStartTag) {
stack.push(tag);
} else if (stack.length > 0) {
let lastTag = stack[stack.length - 1];
if (lastTag === tag) {
stack.pop();
}
}
}
}
if (stack.length > 0) {
let 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 "";
}
}
function moveSelectionRight(selection: vscode.Selection, shift: number): vscode.Selection {
let newPosition = selection.active.translate(0, shift);
let newSelection = new vscode.Selection(newPosition, newPosition);
return newSelection;
}
function occurrenceCount(source: string, find: string): number {
return source.split(find).length - 1;
}

Просмотреть файл

@ -0,0 +1,5 @@
{
"capabilities" : {
"documentFormattingProvider" : "true"
}
}

23
src/test/runTest.ts Normal file
Просмотреть файл

@ -0,0 +1,23 @@
import * as path from 'path';
import { runTests } from '@vscode/test-electron';
async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
// The path to test runner
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');
// Download VS Code, unzip it and run the integration test
await runTests({ extensionDevelopmentPath, extensionTestsPath });
} catch (err) {
console.error('Failed to run tests');
process.exit(1);
}
}
main();

Просмотреть файл

@ -0,0 +1,15 @@
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
// import * as myExtension from '../../extension';
suite('Extension Test Suite', () => {
vscode.window.showInformationMessage('Start all tests.');
test('Sample test', () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
});
});

38
src/test/suite/index.ts Normal file
Просмотреть файл

@ -0,0 +1,38 @@
import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd',
color: true
});
const testsRoot = path.resolve(__dirname, '..');
return new Promise((c, e) => {
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
if (err) {
return e(err);
}
// Add files to the test suite
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
try {
// Run the mocha test
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
}

20
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,20 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"lib": [
"ES2020"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"exclude": [
"node_modules",
".vscode-test"
]
}

48
webpack.config.js Normal file
Просмотреть файл

@ -0,0 +1,48 @@
//@ts-check
'use strict';
const path = require('path');
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
/** @type WebpackConfig */
const extensionConfig = {
target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2'
},
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
// modules added here also need to be added in the .vscodeignore file
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
},
devtool: 'nosources-source-map',
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
};
module.exports = [ extensionConfig ];