Add autocomplete to editor [#4]
This commit is contained in:
parent
4d301adf31
commit
55b5232fa6
29
index.html
29
index.html
|
@ -8,6 +8,9 @@
|
||||||
'self'
|
'self'
|
||||||
'sha256-0SGl1PJNDyJwcV5T+weg2zpEMrh7xvlwO4oXgvZCeZk='
|
'sha256-0SGl1PJNDyJwcV5T+weg2zpEMrh7xvlwO4oXgvZCeZk='
|
||||||
'sha256-eue5ceZRwKVQ1OXOZSyU7MXCTZMlqsPi/TOIqh1Vlzo='
|
'sha256-eue5ceZRwKVQ1OXOZSyU7MXCTZMlqsPi/TOIqh1Vlzo='
|
||||||
|
'sha256-OvxDPyq6KQAoWh11DLJdBVlHHLkYYiy4EzqTjIEJbb4='
|
||||||
|
'sha256-vnm8bzrI+krtz5228JDC2DoTv0e+sfnfTCiUnO2EBAM='
|
||||||
|
'sha256-HYX1RusN7a369vYuOd1mGvxLcNL4z/MihkahAI2CH8k='
|
||||||
;
|
;
|
||||||
style-src 'self' https://cdnjs.cloudflare.com;
|
style-src 'self' https://cdnjs.cloudflare.com;
|
||||||
img-src 'self' blob:;
|
img-src 'self' blob:;
|
||||||
|
@ -23,6 +26,12 @@
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/addon/hint/show-hint.min.css"
|
||||||
|
integrity="sha256-Ng5EdzHS/CC37tR7tE75e4Th9+fBvOB4eYITOkXS22Q="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
>
|
||||||
|
|
||||||
<link rel="stylesheet" href="styles/main.css">
|
<link rel="stylesheet" href="styles/main.css">
|
||||||
|
|
||||||
<script src="scripts/requireConfig.js"></script>
|
<script src="scripts/requireConfig.js"></script>
|
||||||
|
@ -35,11 +44,29 @@
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="cdn-codemirror"
|
name="cdn-cm/lib/codemirror"
|
||||||
content="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/codemirror.min.js"
|
content="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/codemirror.min.js"
|
||||||
data-integrity="sha256-eue5ceZRwKVQ1OXOZSyU7MXCTZMlqsPi/TOIqh1Vlzo="
|
data-integrity="sha256-eue5ceZRwKVQ1OXOZSyU7MXCTZMlqsPi/TOIqh1Vlzo="
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="cdn-cm/addon/hint/show-hint"
|
||||||
|
content="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/addon/hint/show-hint.min.js"
|
||||||
|
data-integrity="sha256-OvxDPyq6KQAoWh11DLJdBVlHHLkYYiy4EzqTjIEJbb4="
|
||||||
|
>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="cdn-cm/addon/edit/trailingspace"
|
||||||
|
content="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/addon/edit/trailingspace.min.js"
|
||||||
|
data-integrity="sha256-vnm8bzrI+krtz5228JDC2DoTv0e+sfnfTCiUnO2EBAM="
|
||||||
|
>
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="cdn-cm/addon/comment/comment"
|
||||||
|
content="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.31.0/addon/comment/comment.min.js"
|
||||||
|
data-integrity="sha256-HYX1RusN7a369vYuOd1mGvxLcNL4z/MihkahAI2CH8k="
|
||||||
|
>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
define(['codemirror'], (CodeMirror) => {
|
define([
|
||||||
|
'cm/lib/codemirror',
|
||||||
|
'cm/addon/hint/show-hint',
|
||||||
|
'cm/addon/edit/trailingspace',
|
||||||
|
'cm/addon/comment/comment',
|
||||||
|
], (CodeMirror) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const DELAY_AGENTCHANGE = 500;
|
const DELAY_AGENTCHANGE = 500;
|
||||||
|
@ -82,27 +87,68 @@ define(['codemirror'], (CodeMirror) => {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
build(container) {
|
buildEditor(container) {
|
||||||
this.codePane = makeNode('div', {'class': 'pane-code'});
|
const value = this.loadCode() || this.defaultCode;
|
||||||
this.viewPane = makeNode('div', {'class': 'pane-view'});
|
|
||||||
this.viewPaneInner = makeNode('div', {'class': 'pane-view-inner'});
|
|
||||||
|
|
||||||
const options = this.buildOptions();
|
|
||||||
this.viewPane.appendChild(this.viewPaneInner);
|
|
||||||
this.viewPane.appendChild(options);
|
|
||||||
|
|
||||||
container.appendChild(this.codePane);
|
|
||||||
container.appendChild(this.viewPane);
|
|
||||||
|
|
||||||
const code = this.loadCode() || this.defaultCode;
|
|
||||||
CodeMirror.defineMode(
|
CodeMirror.defineMode(
|
||||||
'sequence',
|
'sequence',
|
||||||
() => this.parser.getCodeMirrorMode()
|
() => this.parser.getCodeMirrorMode()
|
||||||
);
|
);
|
||||||
this.code = new CodeMirror(this.codePane, {
|
CodeMirror.registerHelper(
|
||||||
value: code,
|
'hint',
|
||||||
|
'sequence',
|
||||||
|
this.parser.getCodeMirrorHints()
|
||||||
|
);
|
||||||
|
const code = new CodeMirror(container, {
|
||||||
|
value,
|
||||||
mode: 'sequence',
|
mode: 'sequence',
|
||||||
|
lineNumbers: true,
|
||||||
|
showTrailingSpace: true,
|
||||||
|
extraKeys: {
|
||||||
|
'Tab': (cm) => cm.execCommand('indentMore'),
|
||||||
|
'Shift-Tab': (cm) => cm.execCommand('indentLess'),
|
||||||
|
'Cmd-/': (cm) => cm.toggleComment({padding: ''}),
|
||||||
|
'Ctrl-/': (cm) => cm.toggleComment({padding: ''}),
|
||||||
|
'Ctrl-Space': 'autocomplete',
|
||||||
|
'Ctrl-Enter': 'autocomplete',
|
||||||
|
'Cmd-Enter': 'autocomplete',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
let lastKey = 0;
|
||||||
|
code.on('keydown', (cm, event) => {
|
||||||
|
lastKey = event.keyCode;
|
||||||
|
});
|
||||||
|
code.on('endCompletion', () => {
|
||||||
|
lastKey = 0;
|
||||||
|
});
|
||||||
|
code.on('change', (cm) => {
|
||||||
|
if(cm.state.completionActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(lastKey === 13 || lastKey === 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastKey = 0;
|
||||||
|
CodeMirror.commands.autocomplete(cm, null, {
|
||||||
|
completeSingle: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(container) {
|
||||||
|
const codePane = makeNode('div', {'class': 'pane-code'});
|
||||||
|
const viewPane = makeNode('div', {'class': 'pane-view'});
|
||||||
|
this.viewPaneInner = makeNode('div', {'class': 'pane-view-inner'});
|
||||||
|
|
||||||
|
const options = this.buildOptions();
|
||||||
|
viewPane.appendChild(this.viewPaneInner);
|
||||||
|
viewPane.appendChild(options);
|
||||||
|
|
||||||
|
container.appendChild(codePane);
|
||||||
|
container.appendChild(viewPane);
|
||||||
|
|
||||||
|
this.code = this.buildEditor(codePane);
|
||||||
this.viewPaneInner.appendChild(this.renderer.svg());
|
this.viewPaneInner.appendChild(this.renderer.svg());
|
||||||
|
|
||||||
this.code.on('change', () => this.update(false));
|
this.code.on('change', () => this.update(false));
|
||||||
|
|
|
@ -8,7 +8,11 @@ defineDescribe('Interface', ['./Interface'], (Interface) => {
|
||||||
let ui = null;
|
let ui = null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
parser = jasmine.createSpyObj('parser', ['parse']);
|
parser = jasmine.createSpyObj('parser', [
|
||||||
|
'parse',
|
||||||
|
'getCodeMirrorMode',
|
||||||
|
'getCodeMirrorHints',
|
||||||
|
]);
|
||||||
parser.parse.and.returnValue({
|
parser.parse.and.returnValue({
|
||||||
meta: {},
|
meta: {},
|
||||||
stages: [],
|
stages: [],
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
define(() => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const TRIMMER = /^([ \t]*)(.*)$/;
|
||||||
|
const SQUASH_START = /^[ \t\r\n:,]/;
|
||||||
|
|
||||||
|
function getHints(cm) {
|
||||||
|
const cur = cm.getCursor();
|
||||||
|
const token = cm.getTokenAt(cur);
|
||||||
|
let partial = token.string;
|
||||||
|
if(token.end > cur.ch) {
|
||||||
|
partial = partial.substr(0, cur.ch - token.start);
|
||||||
|
}
|
||||||
|
const parts = TRIMMER.exec(partial);
|
||||||
|
partial = parts[2];
|
||||||
|
const from = token.start + parts[1].length;
|
||||||
|
|
||||||
|
const continuation = (cur.ch > 0 && token.state.line.length > 0);
|
||||||
|
let comp = (continuation ?
|
||||||
|
token.state.completions :
|
||||||
|
token.state.beginCompletions
|
||||||
|
);
|
||||||
|
if(!continuation) {
|
||||||
|
comp = comp.concat(token.state.knownAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ln = cm.getLine(cur.line);
|
||||||
|
const wordFrom = {line: cur.line, ch: from};
|
||||||
|
const squashFrom = {line: cur.line, ch: from};
|
||||||
|
if(from > 0 && ln[from - 1] === ' ') {
|
||||||
|
squashFrom.ch --;
|
||||||
|
}
|
||||||
|
const wordTo = {line: cur.line, ch: token.end};
|
||||||
|
const list = (comp
|
||||||
|
.filter((opt) => opt.startsWith(partial))
|
||||||
|
.map((opt) => {
|
||||||
|
return {
|
||||||
|
text: opt,
|
||||||
|
displayText: (opt === '\n') ? '<END>' : opt.trim(),
|
||||||
|
className: (opt === '\n') ? 'pick-virtual' : null,
|
||||||
|
from: SQUASH_START.test(opt) ? squashFrom : wordFrom,
|
||||||
|
to: wordTo,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
from: wordFrom,
|
||||||
|
to: wordTo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getHints,
|
||||||
|
};
|
||||||
|
});
|
|
@ -1,114 +1,215 @@
|
||||||
define(['core/ArrayUtilities'], (array) => {
|
define(['core/ArrayUtilities'], (array) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function makeCMCommaBlock(type, exits = {}) {
|
const CM_END = {type: '', suggest: '\n', then: {}};
|
||||||
return {type, then: Object.assign({
|
const CM_ERROR = {type: 'error', then: {'': 0}};
|
||||||
|
|
||||||
|
function makeCMCommaBlock(type, suggest, exits = {}) {
|
||||||
|
return {type, suggest, then: Object.assign({
|
||||||
'': 0,
|
'': 0,
|
||||||
',': {type: 'operator', then: {
|
',': {type: 'operator', suggest: true, then: {
|
||||||
'': 1,
|
'': 1,
|
||||||
}},
|
}},
|
||||||
}, exits)};
|
}, exits)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const CM_TEXT_TO_END = {type: 'string', then: {'': 0}};
|
const CM_TEXT_TO_END = {type: 'string', then: {'': 0, '\n': CM_END}};
|
||||||
const CM_IDENT_LIST_TO_END = makeCMCommaBlock('variable');
|
const CM_AGENT_LIST_TO_END = makeCMCommaBlock('variable', 'Agent', {
|
||||||
const CM_IDENT_LIST_TO_TEXT = makeCMCommaBlock('variable', {
|
'\n': CM_END,
|
||||||
':': {type: 'operator', then: {'': CM_TEXT_TO_END}},
|
});
|
||||||
|
const CM_AGENT_LIST_TO_TEXT = makeCMCommaBlock('variable', 'Agent', {
|
||||||
|
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
||||||
|
});
|
||||||
|
const CM_AGENT_LIST_TO_OPTTEXT = makeCMCommaBlock('variable', 'Agent', {
|
||||||
|
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
||||||
|
'\n': CM_END,
|
||||||
});
|
});
|
||||||
|
|
||||||
const CM_NOTE_SIDE = {type: 'keyword', then: {
|
const CM_NOTE_SIDE_THEN = {
|
||||||
'of': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_TEXT}},
|
'of': {type: 'keyword', suggest: true, then: {
|
||||||
':': {type: 'operator', then: {'': CM_TEXT_TO_END}},
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
'': CM_IDENT_LIST_TO_TEXT,
|
}},
|
||||||
}};
|
':': {type: 'operator', suggest: true, then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
}},
|
||||||
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
|
};
|
||||||
|
|
||||||
const CM_CONNECT = {type: 'keyword', then: {
|
const CM_NOTE_LSIDE = {
|
||||||
'': CM_IDENT_LIST_TO_TEXT,
|
type: 'keyword',
|
||||||
|
suggest: ['left of ', 'left: '],
|
||||||
|
then: CM_NOTE_SIDE_THEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CM_NOTE_RSIDE = {
|
||||||
|
type: 'keyword',
|
||||||
|
suggest: ['right of ', 'right: '],
|
||||||
|
then: CM_NOTE_SIDE_THEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CM_CONNECT = {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_OPTTEXT,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const CM_COMMANDS = {type: 'error', then: {
|
const CM_COMMANDS = {type: 'error', then: {
|
||||||
'title': {type: 'keyword', then: {'': CM_TEXT_TO_END}},
|
'title': {type: 'keyword', suggest: true, then: {
|
||||||
'terminators': {type: 'keyword', then: {
|
|
||||||
'none': {type: 'keyword', then: {}},
|
|
||||||
'cross': {type: 'keyword', then: {}},
|
|
||||||
'box': {type: 'keyword', then: {}},
|
|
||||||
'bar': {type: 'keyword', then: {}},
|
|
||||||
}},
|
|
||||||
'define': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_END}},
|
|
||||||
'begin': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_END}},
|
|
||||||
'end': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_END}},
|
|
||||||
'if': {type: 'keyword', then: {
|
|
||||||
'': CM_TEXT_TO_END,
|
'': CM_TEXT_TO_END,
|
||||||
':': {type: 'operator', then: {'': CM_TEXT_TO_END}},
|
|
||||||
}},
|
}},
|
||||||
'else': {type: 'keyword', then: {
|
'terminators': {type: 'keyword', suggest: true, then: {
|
||||||
'if': {type: 'keyword', then: {
|
'none': {type: 'keyword', suggest: true, then: {}},
|
||||||
|
'cross': {type: 'keyword', suggest: true, then: {}},
|
||||||
|
'box': {type: 'keyword', suggest: true, then: {}},
|
||||||
|
'bar': {type: 'keyword', suggest: true, then: {}},
|
||||||
|
}},
|
||||||
|
'define': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_END,
|
||||||
|
}},
|
||||||
|
'begin': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_END,
|
||||||
|
}},
|
||||||
|
'end': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_END,
|
||||||
|
'\n': CM_END,
|
||||||
|
}},
|
||||||
|
'if': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
':': {type: 'operator', suggest: true, then: {
|
||||||
'': CM_TEXT_TO_END,
|
'': CM_TEXT_TO_END,
|
||||||
':': {type: 'operator', then: {'': CM_TEXT_TO_END}},
|
}},
|
||||||
|
'\n': CM_END,
|
||||||
|
}},
|
||||||
|
'else': {type: 'keyword', suggest: ['else\n', 'else if: '], then: {
|
||||||
|
'if': {type: 'keyword', suggest: 'if: ', then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
':': {type: 'operator', suggest: true, then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
'\n': CM_END,
|
||||||
|
}},
|
||||||
|
'repeat': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
':': {type: 'operator', suggest: true, then: {
|
||||||
|
'': CM_TEXT_TO_END,
|
||||||
|
}},
|
||||||
|
'\n': CM_END,
|
||||||
|
}},
|
||||||
|
'note': {type: 'keyword', suggest: true, then: {
|
||||||
|
'over': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
|
}},
|
||||||
|
'left': CM_NOTE_LSIDE,
|
||||||
|
'right': CM_NOTE_RSIDE,
|
||||||
|
'between': {type: 'keyword', suggest: true, then: {
|
||||||
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
'repeat': {type: 'keyword', then: {
|
'state': {type: 'keyword', suggest: 'state over ', then: {
|
||||||
'': CM_TEXT_TO_END,
|
'over': {type: 'keyword', suggest: true, then: {
|
||||||
':': {type: 'operator', then: {'': CM_TEXT_TO_END}},
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
|
}},
|
||||||
}},
|
}},
|
||||||
'note': {type: 'keyword', then: {
|
'text': {type: 'keyword', suggest: true, then: {
|
||||||
'over': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_TEXT}},
|
'left': CM_NOTE_LSIDE,
|
||||||
'left': CM_NOTE_SIDE,
|
'right': CM_NOTE_RSIDE,
|
||||||
'right': CM_NOTE_SIDE,
|
|
||||||
'between': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_TEXT}},
|
|
||||||
}},
|
}},
|
||||||
'state': {type: 'keyword', then: {
|
'simultaneously': {type: 'keyword', suggest: true, then: {
|
||||||
'over': {type: 'keyword', then: {'': CM_IDENT_LIST_TO_TEXT}},
|
':': {type: 'operator', suggest: true, then: {}},
|
||||||
}},
|
'with': {type: 'keyword', suggest: true, then: {
|
||||||
'text': {type: 'keyword', then: {
|
'': {type: 'variable', suggest: 'Label', then: {
|
||||||
'left': CM_NOTE_SIDE,
|
|
||||||
'right': CM_NOTE_SIDE,
|
|
||||||
}},
|
|
||||||
'simultaneously': {type: 'keyword', then: {
|
|
||||||
':': {type: 'operator', then: {}},
|
|
||||||
'with': {type: 'keyword', then: {
|
|
||||||
'': {type: 'variable', then: {
|
|
||||||
'': 0,
|
'': 0,
|
||||||
':': {type: 'operator', then: {}},
|
':': {type: 'operator', suggest: true, then: {}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
'': {type: 'variable', then: {
|
'': {type: 'variable', suggest: 'Agent', then: {
|
||||||
'->': CM_CONNECT,
|
'->': CM_CONNECT,
|
||||||
'-->': CM_CONNECT,
|
'-->': CM_CONNECT,
|
||||||
'<-': CM_CONNECT,
|
'<-': CM_CONNECT,
|
||||||
'<--': CM_CONNECT,
|
'<--': CM_CONNECT,
|
||||||
'<->': CM_CONNECT,
|
'<->': CM_CONNECT,
|
||||||
'<-->': CM_CONNECT,
|
'<-->': CM_CONNECT,
|
||||||
':': {type: 'operator', then: {}},
|
':': {type: 'operator', suggest: true, override: 'Label', then: {}},
|
||||||
'': 0,
|
'': 0,
|
||||||
}},
|
}},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
function cmCheckToken(state, partial) {
|
function cmGetSuggestions(state, token, {suggest, then}) {
|
||||||
if(!partial && state.current === '\n') {
|
if(token === '') {
|
||||||
// quoted newline is interpreted as a command separator;
|
return state['known' + suggest];
|
||||||
// probably not what the writer expected, so highlight it
|
} else if(suggest === true) {
|
||||||
state.line.length = 0;
|
if(Object.keys(then).length > 0) {
|
||||||
return 'warning';
|
return [token + ' '];
|
||||||
|
} else {
|
||||||
|
return [token + '\n'];
|
||||||
|
}
|
||||||
|
} else if(Array.isArray(suggest)) {
|
||||||
|
return suggest;
|
||||||
|
} else if(suggest) {
|
||||||
|
return [suggest];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmMakeCompletions(state, path) {
|
||||||
|
const comp = [];
|
||||||
|
const {then} = array.last(path);
|
||||||
|
Object.keys(then).forEach((token) => {
|
||||||
|
let next = then[token];
|
||||||
|
if(typeof next === 'number') {
|
||||||
|
next = path[path.length - next - 1];
|
||||||
|
}
|
||||||
|
array.mergeSets(comp, cmGetSuggestions(state, token, next));
|
||||||
|
});
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSuggestion(state, locals, token, {suggest, override}) {
|
||||||
|
if(locals.type) {
|
||||||
|
if(suggest !== locals.type) {
|
||||||
|
if(override) {
|
||||||
|
locals.type = override;
|
||||||
|
}
|
||||||
|
array.mergeSets(
|
||||||
|
state['known' + locals.type],
|
||||||
|
[locals.value]
|
||||||
|
);
|
||||||
|
locals.type = '';
|
||||||
|
} else {
|
||||||
|
locals.value += token + ' ';
|
||||||
|
}
|
||||||
|
} else if(typeof suggest === 'string' && state['known' + suggest]) {
|
||||||
|
locals.type = suggest;
|
||||||
|
locals.value = token + ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmCheckToken(state, eol) {
|
||||||
|
const suggestions = {
|
||||||
|
type: '',
|
||||||
|
value: '',
|
||||||
|
};
|
||||||
let current = CM_COMMANDS;
|
let current = CM_COMMANDS;
|
||||||
const path = [current];
|
const path = [current];
|
||||||
for(let i = 0; i < state.line.length; ++ i) {
|
|
||||||
const token = state.line[i];
|
state.line.forEach((token, i) => {
|
||||||
let found = current.then[token] || current.then[''];
|
if(i === state.line.length - 1) {
|
||||||
if(found === undefined) {
|
state.completions = cmMakeCompletions(state, path);
|
||||||
return 'error';
|
|
||||||
}
|
}
|
||||||
|
const found = current.then[token] || current.then[''];
|
||||||
if(typeof found === 'number') {
|
if(typeof found === 'number') {
|
||||||
path.length -= found;
|
path.length -= found;
|
||||||
current = array.last(path);
|
|
||||||
} else {
|
} else {
|
||||||
path.push(found);
|
path.push(found || CM_ERROR);
|
||||||
current = found;
|
|
||||||
}
|
}
|
||||||
|
current = array.last(path);
|
||||||
|
updateSuggestion(state, suggestions, token, current);
|
||||||
|
});
|
||||||
|
if(eol) {
|
||||||
|
updateSuggestion(state, suggestions, '', {});
|
||||||
}
|
}
|
||||||
|
state.nextCompletions = cmMakeCompletions(state, path);
|
||||||
return current.type;
|
return current.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +223,11 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return {
|
return {
|
||||||
currentType: -1,
|
currentType: -1,
|
||||||
current: '',
|
current: '',
|
||||||
|
knownAgent: [],
|
||||||
|
knownLabel: [],
|
||||||
|
beginCompletions: cmMakeCompletions({}, [CM_COMMANDS]),
|
||||||
|
completions: [],
|
||||||
|
nextCompletions: [],
|
||||||
line: [],
|
line: [],
|
||||||
indent: 0,
|
indent: 0,
|
||||||
};
|
};
|
||||||
|
@ -165,7 +271,13 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return 'comment';
|
return 'comment';
|
||||||
}
|
}
|
||||||
state.line.push(state.current);
|
state.line.push(state.current);
|
||||||
return cmCheckToken(state, false);
|
if(state.current === '\n') {
|
||||||
|
// quoted newline is interpreted as a command separator;
|
||||||
|
// probably not what the writer expected, so highlight it
|
||||||
|
state.line.length = 0;
|
||||||
|
return 'warning';
|
||||||
|
}
|
||||||
|
return cmCheckToken(state, stream.eol());
|
||||||
}
|
}
|
||||||
|
|
||||||
_tokenEOLFound(stream, state, block) {
|
_tokenEOLFound(stream, state, block) {
|
||||||
|
@ -174,7 +286,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return 'comment';
|
return 'comment';
|
||||||
}
|
}
|
||||||
state.line.push(state.current);
|
state.line.push(state.current);
|
||||||
const type = cmCheckToken(state, true);
|
const type = cmCheckToken(state, false);
|
||||||
state.line.pop();
|
state.line.pop();
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -194,6 +306,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
token(stream, state) {
|
token(stream, state) {
|
||||||
|
state.completions = state.nextCompletions;
|
||||||
if(stream.sol() && state.currentType === -1) {
|
if(stream.sol() && state.currentType === -1) {
|
||||||
state.line.length = 0;
|
state.line.length = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
meta: {
|
meta: {
|
||||||
title: meta.title,
|
title: meta.title,
|
||||||
},
|
},
|
||||||
agents: this.agents,
|
agents: this.agents.slice(),
|
||||||
stages: globals.stages,
|
stages: globals.stages,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
define(['core/ArrayUtilities', './CodeMirrorMode'], (array, CMMode) => {
|
define([
|
||||||
|
'core/ArrayUtilities',
|
||||||
|
'./CodeMirrorMode',
|
||||||
|
'./CodeMirrorHints',
|
||||||
|
], (
|
||||||
|
array,
|
||||||
|
CMMode,
|
||||||
|
CMHints
|
||||||
|
) => {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function execAt(str, reg, i) {
|
function execAt(str, reg, i) {
|
||||||
|
@ -355,6 +363,10 @@ define(['core/ArrayUtilities', './CodeMirrorMode'], (array, CMMode) => {
|
||||||
return new CMMode(TOKENS);
|
return new CMMode(TOKENS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCodeMirrorHints() {
|
||||||
|
return CMHints.getHints;
|
||||||
|
}
|
||||||
|
|
||||||
splitLines(tokens) {
|
splitLines(tokens) {
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let line = [];
|
let line = [];
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
define([], () => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
define([], () => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
define([], () => {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
|
@ -13,6 +13,7 @@ define([], () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeMirror.defineMode = () => null;
|
CodeMirror.defineMode = () => null;
|
||||||
|
CodeMirror.registerHelper = () => null;
|
||||||
|
|
||||||
return CodeMirror;
|
return CodeMirror;
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,6 +24,17 @@ html, body {
|
||||||
.cm-s-default .cm-string {color: #221111;}
|
.cm-s-default .cm-string {color: #221111;}
|
||||||
.cm-s-default .cm-error {color: #FF0000;}
|
.cm-s-default .cm-error {color: #FF0000;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-warning {
|
||||||
|
background: #FFFF00;
|
||||||
|
}
|
||||||
|
.cm-s-default .cm-trailingspace {
|
||||||
|
background: rgba(255, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pick-virtual {
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
|
||||||
.pane-view {
|
.pane-view {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 30%;
|
left: 30%;
|
||||||
|
|
5
test.htm
5
test.htm
|
@ -57,7 +57,10 @@
|
||||||
data-integrity="sha256-Re9XxIL3x1flvE6WD58jWPdDzKYQLXwxS2HAVfmM6Z8="
|
data-integrity="sha256-Re9XxIL3x1flvE6WD58jWPdDzKYQLXwxS2HAVfmM6Z8="
|
||||||
>
|
>
|
||||||
|
|
||||||
<meta name="cdn-codemirror" content="stubs/codemirror">
|
<meta name="cdn-cm/lib/codemirror" content="stubs/codemirror">
|
||||||
|
<meta name="cdn-cm/addon/hint/show-hint" content="stubs/codemirror-show-hint">
|
||||||
|
<meta name="cdn-cm/addon/edit/trailingspace" content="stubs/codemirror-trailingspace">
|
||||||
|
<meta name="cdn-cm/addon/comment/comment" content="stubs/codemirror-comment">
|
||||||
|
|
||||||
<!-- test files defined in scripts/specs.js -->
|
<!-- test files defined in scripts/specs.js -->
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue