First steps of UI rewrite, broken.
This commit is contained in:
Родитель
e37166cc59
Коммит
978afa88be
|
@ -6,5 +6,13 @@
|
|||
"spaceUnits": 4,
|
||||
"brackets-git.gitPath": "git"
|
||||
}
|
||||
},
|
||||
"language": {
|
||||
"javascript": {
|
||||
"linting.prefer": [
|
||||
"JSHint"
|
||||
],
|
||||
"linting.usePreferredOnly": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,17 @@
|
|||
{
|
||||
"browser": true,
|
||||
"devel": true,
|
||||
"jquery": true,
|
||||
"globals": {
|
||||
"moment": false,
|
||||
"angular": false
|
||||
},
|
||||
|
||||
"esnext": true,
|
||||
"camelcase": true,
|
||||
"eqeqeq": true,
|
||||
"indent": 2,
|
||||
"eqnull": true,
|
||||
"indent": 4,
|
||||
"noempty": true,
|
||||
"nonew": true,
|
||||
"plusplus": true,
|
||||
"quotmark": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": true,
|
||||
"trailing": true,
|
||||
"laxbreak": true
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
<action type="Rewrite" url="wwwroot/index.html" />
|
||||
</rule>
|
||||
<rule name="App" stopProcessing="true">
|
||||
<match url="^((.+)\.(?:html|js|css))$" />
|
||||
<match url="^((.+)\.(?:html|js|css|js\.map|json))$" />
|
||||
<action type="Rewrite" url="wwwroot/{R:1}" />
|
||||
</rule>
|
||||
</rules>
|
||||
|
|
|
@ -1,34 +1,40 @@
|
|||
var gulp = require('gulp');
|
||||
var concat = require('gulp-concat');
|
||||
var plumber = require('gulp-plumber');
|
||||
var uglify = require('gulp-uglify');
|
||||
var less = require('gulp-less');
|
||||
var htmlreplace = require('gulp-html-replace');
|
||||
var webpack = require('webpack-stream');
|
||||
var assign = require('object-assign');
|
||||
|
||||
gulp.task('less', function () {
|
||||
return gulp
|
||||
.src('./legacy/app.less')
|
||||
.pipe(less())
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
return gulp
|
||||
.src('./legacy/app.less')
|
||||
.pipe(less())
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
});
|
||||
|
||||
gulp.task('js', function () {
|
||||
return gulp
|
||||
.src(['./legacy/external/**/*.js', './legacy/app.js', './legacy/**/*.js'])
|
||||
.pipe(concat('app.js'))
|
||||
.pipe(uglify())
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
var config = require('./webpack.config.js');
|
||||
|
||||
return gulp
|
||||
.src('./js/app.js')
|
||||
.pipe(webpack(assign({}, config, {
|
||||
output: { filename: "app.min.js" }
|
||||
})))
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
});
|
||||
|
||||
gulp.task('html', function () {
|
||||
return gulp
|
||||
.src('./legacy/index.html')
|
||||
.pipe(htmlreplace({ js: 'app.js', css: 'app.css' }))
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
return gulp
|
||||
.src('./index.html')
|
||||
.pipe(htmlreplace({ js: 'app.min.js', css: 'app.css' }))
|
||||
.pipe(gulp.dest('wwwroot'));
|
||||
});
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch('**/*.less', ['less']);
|
||||
gulp.task('watch', ['default'], function () {
|
||||
gulp.watch('legacy/**/*.less', ['less']);
|
||||
gulp.watch('js/**/*.js', ['js']);
|
||||
gulp.watch('index.html', ['html']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['less', 'js', 'html']);
|
|
@ -0,0 +1,166 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Try Roslyn</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta name="theme-color" content="#4684ee">
|
||||
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.0.3/codemirror.min.css"/>
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/4.0.3//addon/lint/lint.css" />
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.0/normalize.min.css" />
|
||||
<!-- build:css -->
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="loader/loader.css"/>
|
||||
<!-- endbuild -->
|
||||
</head>
|
||||
<body data-app-mobile-codemirror-focus="mobile-editor-focus">
|
||||
<main v-bind:class="{failed: !result.success}">
|
||||
<div class="section-group">
|
||||
<section class="code" data-app-mobile-codemirror-focus="mobile-editor-focus">
|
||||
<header>
|
||||
<input class="mobile-menu-button" type="button" />
|
||||
<h1>Code</h1>
|
||||
<div class="languages-and-modes">
|
||||
<div class="select-wrapper option-language option">
|
||||
<select v-model="options.language">
|
||||
<option value="csharp">C#</option>
|
||||
<option value="vbnet">VB.NET</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="select-wrapper option-mode option">
|
||||
<select v-model="options.mode">
|
||||
<option value="regular">Standard</option>
|
||||
<option value="script">Script</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="select-wrapper option-branch option">
|
||||
<select data-v-model="branch">
|
||||
<option value="">Release (NuGet)</option>
|
||||
<option v-for="branch in branches" value="{{branch.id}}">{{branch.displayText}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</header>
|
||||
<div class="mobile-menu" data-app-mobile-shelf="{
|
||||
toggle: '.code .mobile-menu-button',
|
||||
container: '.code',
|
||||
openClass: 'mobile-menu-open'
|
||||
}"></div>
|
||||
<div class="content">
|
||||
<app-codemirror v-bind:value="code"
|
||||
v-bind:mode="codeMirrorModes[options.language]"
|
||||
v-bind:lint="processCodeAsync"
|
||||
v-bind:options="{
|
||||
lineNumbers: true,
|
||||
indentUnit: 4,
|
||||
gutters: ['CodeMirror-linenumbers','CodeMirror-lint-markers']
|
||||
}"></app-codemirror>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="branch" class="info-only non-code">
|
||||
<header>
|
||||
<h1>Branch {{branch.name}}, last commit</h1>
|
||||
</header>
|
||||
<div class="content">
|
||||
<div>
|
||||
<a href="https://github.com/dotnet/roslyn/commit/{{branch.commits[0].hash}}" target="_blank">{{branch.commits[0].hash}}</a>
|
||||
at {{branch.commits[0].date | date:'dd MMM yyyy'}}
|
||||
by {{branch.commits[0].author}}
|
||||
</div>
|
||||
<div class="commit-message">{{branch.commits[0].message | trim}}</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="section-group results">
|
||||
<section v-show="result.success" class="decompiled" data-app-mobile-codemirror-focus="mobile-editor-focus">
|
||||
<header>
|
||||
<input class="mobile-menu-button" type="button"/>
|
||||
<h1>Decompiled</h1>
|
||||
<!--span class="loader-container" data-rv-show="loading" data-ng-include="'loader/loader.html'">
|
||||
</span-->
|
||||
|
||||
<div class="select-wrapper option-target-language option">
|
||||
<select v-model="options.target">
|
||||
<option value="csharp">C#</option>
|
||||
<option value="vbnet">VB.NET</option>
|
||||
<option value="il">IL</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="select-wrapper option-optimizations option">
|
||||
<select v-model="options.optimizations" data-app-boolean-model>
|
||||
<option value="false">Debug</option>
|
||||
<option value="true">Release</option>
|
||||
</select>
|
||||
</div>
|
||||
</header>
|
||||
<div class="mobile-menu" data-app-mobile-shelf="{
|
||||
toggle: '.decompiled .mobile-menu-button',
|
||||
container: '.decompiled',
|
||||
openClass: 'mobile-menu-open'
|
||||
}"></div>
|
||||
<div class="content">
|
||||
<app-codemirror v-bind:value="result.decompiled"
|
||||
v-bind:mode="codeMirrorModes[options.target]"
|
||||
v-bind:options="{ readOnly: true, indentUnit: 4 }"></app-codemirror>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="errors" v-show="result.errors">
|
||||
<header>
|
||||
<h1>Errors</h1>
|
||||
<!--span class="loader-container" data-ng-show="loading" data-ng-include="'loader/loader.html'">
|
||||
</span-->
|
||||
</header>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li data-rv-each-error="result.errors">
|
||||
<app-diagnostic bind="error"></app-diagnostic>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="warnings" v-show="warningsVisible">
|
||||
<header>
|
||||
<h1>Warnings</h1>
|
||||
</header>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li data-rv-each-warning="result.warnings">
|
||||
<app-diagnostic bind="warning"></app-diagnostic>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Built by Andrey Shchekin (<a href="http://twitter.com/ashmind">@ashmind</a>). See <a href="http://github.com/ashmind/TryRoslyn">TryRoslyn</a> on GitHub.
|
||||
</footer>
|
||||
|
||||
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-throttle-debounce/1.1/jquery.ba-throttle-debounce.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.4/hammer.min.js"></script>
|
||||
|
||||
<!-- build:js -->
|
||||
<!-- endbuild -->
|
||||
|
||||
<!--script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-1782671-10', 'tryroslyn.azurewebsites.net');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script-->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,99 @@
|
|||
import CodeMirror from 'codemirror';
|
||||
|
||||
import getBranchesAsync from './server/get-branches-async.js';
|
||||
import sendCodeAsync from './server/send-code-async.js';
|
||||
import uiAsync from './ui/main.js';
|
||||
|
||||
import urlState from './state/url.js';
|
||||
|
||||
let pendingRequest;
|
||||
async function processCodeAsync(code) {
|
||||
this.code = code;
|
||||
if (code === undefined || code === '')
|
||||
return [];
|
||||
|
||||
urlState.save(this.code, this.options);
|
||||
if (pendingRequest) {
|
||||
pendingRequest.abort();
|
||||
pendingRequest = null;
|
||||
}
|
||||
|
||||
const branchUrl = this.branch ? this.branch.url : null;
|
||||
|
||||
this.loading = true;
|
||||
const resultPromise = sendCodeAsync(code, this.options, branchUrl);
|
||||
pendingRequest = resultPromise;
|
||||
|
||||
try {
|
||||
this.result = await resultPromise;
|
||||
}
|
||||
catch (ex) {
|
||||
console.log(ex);
|
||||
const error = ex.response.data;
|
||||
let report = error.exceptionMessage || error.message;
|
||||
if (error.stackTrace)
|
||||
report += '\r\n' + error.stackTrace;
|
||||
|
||||
this.result = {
|
||||
success: false,
|
||||
errors: [ report ]
|
||||
};
|
||||
}
|
||||
|
||||
if (pendingRequest === resultPromise)
|
||||
pendingRequest = null;
|
||||
this.loading = false;
|
||||
return convertToAnnotations(
|
||||
this.result.errors,
|
||||
this.result.warnings
|
||||
);
|
||||
}
|
||||
|
||||
function convertToAnnotations(errors, warnings) {
|
||||
const annotations = [];
|
||||
const pushAnnotations = array => {
|
||||
if (!array)
|
||||
return;
|
||||
|
||||
for (let item of array) {
|
||||
annotations.push({
|
||||
severity: item.severity.toLowerCase(),
|
||||
message: item.message,
|
||||
from: CodeMirror.Pos(item.start.line, item.start.column),
|
||||
to: CodeMirror.Pos(item.end.line, item.end.column)
|
||||
});
|
||||
}
|
||||
}
|
||||
pushAnnotations(errors);
|
||||
pushAnnotations(warnings);
|
||||
return annotations;
|
||||
}
|
||||
|
||||
(async function init() {
|
||||
const application = Object.assign({
|
||||
codeMirrorModes: {
|
||||
csharp: 'text/x-csharp',
|
||||
vbnet: 'text/x-vb',
|
||||
il: ''
|
||||
},
|
||||
|
||||
branches: (await getBranchesAsync()),
|
||||
branch: null,
|
||||
|
||||
result: {
|
||||
success: true,
|
||||
decompiled: ''
|
||||
}
|
||||
}, urlState.load() || {
|
||||
options: {
|
||||
language: 'csharp',
|
||||
mode: 'regular',
|
||||
target: 'csharp',
|
||||
optimizations: null
|
||||
},
|
||||
code: ''
|
||||
});
|
||||
|
||||
application.processCodeAsync = processCodeAsync.bind(application);
|
||||
await uiAsync(application);
|
||||
})();
|
|
@ -0,0 +1,10 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default async function getBranchesAsync() {
|
||||
try {
|
||||
return await $.get('!branches.json');
|
||||
}
|
||||
catch(e) {
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
export default function sendCodeAsync(code, options, branchUrl) {
|
||||
let url = 'api/compilation';
|
||||
if (branchUrl)
|
||||
url = branchUrl.replace(/\/?$/, '/') + url;
|
||||
|
||||
const data = Object.assign({ code: code }, options);
|
||||
return $.post(url, data);
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
import $ from 'jquery';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
let lastHash;
|
||||
function save(code, options) {
|
||||
let hash = LZString.compressToBase64(code);
|
||||
const flags = stringifyFlags(options);
|
||||
if (flags)
|
||||
hash = 'f:' + flags + '/' + hash;
|
||||
|
||||
if (options.branch)
|
||||
hash = 'b:' + options.branch + '/' + hash;
|
||||
|
||||
lastHash = hash;
|
||||
window.location.hash = hash;
|
||||
}
|
||||
|
||||
function loadInternal(onlyIfChanged) {
|
||||
let hash = window.location.hash;
|
||||
if (!hash)
|
||||
return null;
|
||||
|
||||
hash = hash.replace(/^#/, '');
|
||||
if (!hash || (onlyIfChanged && hash === lastHash))
|
||||
return null;
|
||||
|
||||
lastHash = hash;
|
||||
const match = /(?:b:([^\/]+)\/)?(?:f:([^\/]+)\/)?(.+)/.exec(hash);
|
||||
if (match === null)
|
||||
return null;
|
||||
|
||||
const result = {
|
||||
options: Object.assign({ branch: match[1] }, parseFlags(match[2]))
|
||||
};
|
||||
|
||||
try {
|
||||
result.code = LZString.decompressFromBase64(match[3]);
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function load() {
|
||||
return loadInternal(false);
|
||||
}
|
||||
|
||||
function onchange(callback) {
|
||||
$(window).on('hashchange', () => {
|
||||
const loaded = loadInternal(true);
|
||||
if (loaded !== null)
|
||||
callback(loaded);
|
||||
});
|
||||
}
|
||||
|
||||
var targetMap = { csharp: '', vbnet: '>vb', il: '>il' };
|
||||
var targetMapReverse = (() => {
|
||||
const result = {};
|
||||
for (let key in targetMap) {
|
||||
result[targetMap[key]] = key;
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
function stringifyFlags(options) {
|
||||
return [
|
||||
options.language === 'vbnet' ? 'vb' : '',
|
||||
targetMap[options.target],
|
||||
options.mode === 'script' ? 's' : '',
|
||||
options.optimizations ? 'r' : ''
|
||||
].join('');
|
||||
}
|
||||
|
||||
function parseFlags(flags) {
|
||||
if (!flags)
|
||||
return {};
|
||||
|
||||
let target = targetMapReverse[''];
|
||||
for (var key in targetMapReverse) {
|
||||
if (key === '')
|
||||
continue;
|
||||
|
||||
if (flags.indexOf(key) > -1)
|
||||
target = targetMapReverse[key];
|
||||
}
|
||||
|
||||
return {
|
||||
language: /(^|[a-z])vb/.test(flags) ? 'vbnet' : 'csharp',
|
||||
target: target,
|
||||
mode: flags.indexOf('s') > -1 ? 'script' : 'regular',
|
||||
optimizations: flags.indexOf('r') > -1
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
save,
|
||||
load,
|
||||
onchange
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import Vue from 'vue';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/addon/lint/lint';
|
||||
import 'codemirror/mode/clike/clike';
|
||||
import 'codemirror/mode/vb/vb';
|
||||
|
||||
|
||||
function buildGetAnnotations(data) {
|
||||
return async (cm, updateLinting) => {
|
||||
updateLinting(await data.lint(data.value));
|
||||
};
|
||||
}
|
||||
|
||||
Vue.component('app-codemirror', {
|
||||
props: {
|
||||
value: String,
|
||||
mode: String,
|
||||
lint: Function,
|
||||
options: Object
|
||||
},
|
||||
ready: function() {
|
||||
const textarea = this.$el;
|
||||
textarea.value = this.value;
|
||||
const options = Object.assign(
|
||||
{},
|
||||
this.options,
|
||||
this.mode !== undefined ? { mode: this.mode } : {},
|
||||
this.lint !== undefined ? {
|
||||
lint: { async: true, getAnnotations: buildGetAnnotations(this) }
|
||||
} : {}
|
||||
);
|
||||
const instance = CodeMirror.fromTextArea(textarea, options);
|
||||
this.$watch('mode', value => instance.setOption('mode', value));
|
||||
|
||||
let settingValue = false;
|
||||
this.$watch('value', value => {
|
||||
value = value != null ? value : '';
|
||||
if (instance.getValue() === value)
|
||||
return;
|
||||
|
||||
settingValue = true;
|
||||
instance.setValue(value);
|
||||
settingValue = false;
|
||||
});
|
||||
|
||||
instance.on('change', () => {
|
||||
if (settingValue)
|
||||
return;
|
||||
|
||||
this.value = instance.getValue();
|
||||
});
|
||||
},
|
||||
template: '<textarea></textarea>'
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import $ from 'jquery';
|
||||
|
||||
import './app-codemirror.js';
|
||||
|
||||
export default function(model) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
$(function() {
|
||||
try {
|
||||
// jshint -W031
|
||||
new Vue({
|
||||
el: 'body',
|
||||
data: model
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,10 +1,25 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.4.5",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-plugin-syntax-async-functions": "^6.3.13",
|
||||
"babel-plugin-transform-regenerator": "^6.4.4",
|
||||
"babel-plugin-transform-runtime": "^6.7.5",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-html-replace": "^1.5.5",
|
||||
"gulp-less": "^3.0.5",
|
||||
"gulp-plumber": "^0.6.6",
|
||||
"gulp-uglify": "^1.5.1"
|
||||
"gulp-uglify": "^1.5.1",
|
||||
"object-assign": "^4.0.1",
|
||||
"webpack-stream": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"codemirror": "^5.13.4",
|
||||
"dateformat": "^1.0.12",
|
||||
"lz-string": "^1.4.4",
|
||||
"regenerator": "^0.8.42",
|
||||
"vue": "^1.0.21"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* globals module:false, require:false */
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
externals: {
|
||||
jquery: 'jQuery'
|
||||
},
|
||||
devtool: 'source-map',
|
||||
plugins: [
|
||||
//new webpack.optimize.UglifyJsPlugin()
|
||||
],
|
||||
entry: [
|
||||
'regenerator/runtime',
|
||||
'./js/app.js'
|
||||
],
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['es2015'],
|
||||
plugins: ['syntax-async-functions', 'transform-regenerator']
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
Загрузка…
Ссылка в новой задаче