Commit fc829ee5 authored by Jesse McGrew's avatar Jesse McGrew
Browse files

Removed bracket coloring feature.

parent 157f22a6adf8
......@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Updated the repository URL. This project is now [hosted on Heptapod](https://foss.heptapod.net/zilf/vscode-zil-language).
### Removed
- Removed bracket coloring. This is provided by other extensions, like
[Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2).
### Fixed
- Fixed schema validation errors when parsing `tasks.json` when the file contains
......
......@@ -7,7 +7,6 @@ This extension adds editing support for ZIL—the Zork Implementation Langua
This extension implements:
* Syntax highlighting
* With "rainbow bracket" coloration to indicate nesting levels, based on Clojure Warrior by Nikita Prokopov
* Snippets
* Hover help
* Signature help
......@@ -42,20 +41,12 @@ This extension contributes the following settings, which can be customized per f
* `zil.compiler.path`: path to `zilf.exe`
* `zil.assembler.path`: path to `zapf.exe`
* `zil.debugger.path`: path to `ConsoleZLR.exe` (optional)
* `zil.rainbowBrackets.enabled`: enable/disable the "rainbow brackets" feature
* `zil.rainbowBrackets.bracketColors`: list of colors to use for rainbow brackets
* `zil.rainbowBrackets.cycleBracketColors`: enable/disable repeating colors for very deep nesting levels
* `zil.misplacedBracketStyle`: appearance of misplaced brackets
* `zil.matchedBracketStyle`: appearance of the matched pair of brackets at the cursor
## Known Issues
### IntelliSense
* Definitions in ZIL code are only reindexed when the file is saved.
### Editor
* When this extension's bracket highlighting conflicts with VS Code's, both pairs are highlighted.
### Debugger
* The debugger has only been tested on Windows.
* Breakpoints can't be set while the game is running.
......
......@@ -86,8 +86,6 @@
"onCommand:zil.debugger.getDebugFilePath",
"onCommand:zil.debugger.getOutputPathWithoutExtension",
"onCommand:zil.debugger.getOutputZVersion",
"onCommand:zil.jumpToMatchingBracket",
"onCommand:zil.selectToMatchingBracket",
"onCommand:zil.startREPL",
"onCommand:workbench.action.tasks.runTask",
"onDebug",
......@@ -257,73 +255,6 @@
"description": "Path to the ZLR debug interpreter (e.g. consolezlr.exe)",
"default": null
},
"zil.rainbowBrackets.enabled": {
"type": "boolean",
"default": true,
"description": "Whether to color brackets to indicate nesting level",
"scope": "resource"
},
"zil.rainbowBrackets.bracketColors": {
"type": "array",
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
"type": "string"
}
}
]
},
"default": [
[
"#000",
"#ccc"
],
"#0098e6",
"#e16d6d",
"#3fa455",
"#c968e6",
"#999",
"#ce7e00"
],
"description": "Array of colors to use for nested brackets",
"scope": "resource"
},
"zil.rainbowBrackets.cycleBracketColors": {
"type": "boolean",
"default": true,
"description": "If nesting level is greater than amount of colors, start counting over",
"scope": "resource"
},
"zil.misplacedBracketStyle": {
"type": "object",
"default": {
"light": {
"color": "#fff",
"backgroundColor": "#c33"
},
"dark": {
"color": "#ccc",
"backgroundColor": "#933"
}
},
"description": "Style of misplaced bracket",
"scope": "resource"
},
"zil.matchedBracketStyle": {
"type": "object",
"default": {
"border": "2px solid #888"
},
"description": "Style of pair bracket highlight",
"scope": "resource"
},
"zil.trace": {
"type": "boolean",
"default": false,
......@@ -337,42 +268,14 @@
]
}
],
"configurationDefaults": {
"[zil]": {
"editor.matchBrackets": false
}
},
"commands": [
{
"command": "zil.startREPL",
"title": "Start REPL",
"category": "ZIL"
},
{
"command": "zil.jumpToMatchingBracket",
"title": "Jump to matching bracket",
"category": "ZIL"
},
{
"command": "zil.selectToMatchingBracket",
"title": "Select to matching bracket",
"category": "ZIL"
}
],
"keybindings": [
{
"command": "zil.jumpToMatchingBracket",
"when": "editorTextFocus && editorLangId == zil",
"key": "ctrl+shift+\\",
"mac": "cmd+shift+\\"
},
{
"command": "zil.selectToMatchingBracket",
"when": "editorTextFocus && editorLangId == zil",
"key": "ctrl+shift+alt+\\",
"mac": "cmd+shift+alt+\\"
}
],
"keybindings": [],
"languages": [
{
"id": "zil",
......
/* Based on Clojure Warrior, copyright (c) 2017 Nikita Prokopov, MIT licensed */
// tslint:disable:variable-name
import isEqual = require("lodash.isequal");
import { isArray } from "util";
import * as vscode from "vscode";
import { Position, Range, Selection } from "vscode";
interface ZilTextEditor extends vscode.TextEditor {
document: ZilTextDocument;
}
interface ZilTextDocument extends vscode.TextDocument {
languageId: "zil";
}
type BracketColor = string | vscode.ThemeColor | Array<string | vscode.ThemeColor>;
type OpenBracket = "(" | "[" | "{" | "<";
type CloseBracket = ")" | "]" | "}" | ">";
export default function startBrackets(context: vscode.ExtensionContext) {
const pairs: { [k in CloseBracket]: OpenBracket; } = { ")": "(", "]": "[", "}": "{", ">": "<" };
function opening(char: string): char is OpenBracket {
return char === "(" || char === "[" || char === "{" || char === "<";
}
function closing(char: string): char is CloseBracket {
return char === ")" || char === "]" || char === "}" || char === ">";
}
function position_str(pos: Position) { return "" + pos.line + ":" + pos.character; }
function is_zil(editor: vscode.TextEditor | undefined): editor is ZilTextEditor {
return !!editor && editor.document.languageId === "zil";
}
context.subscriptions.push(
vscode.commands.registerCommand("zil.jumpToMatchingBracket", jumpToMatchingBracket),
vscode.commands.registerCommand("zil.selectToMatchingBracket", selectToMatchingBracket),
);
let activeEditor: vscode.TextEditor | undefined = vscode.window.activeTextEditor;
let rainbowColors: BracketColor[] = [];
let rainbowTypes: vscode.TextEditorDecorationType[] = [];
let cycleBracketColors: boolean;
let misplacedBracketStyle: vscode.DecorationRenderOptions;
let misplacedType: vscode.TextEditorDecorationType;
let matchedBracketStyle: vscode.DecorationRenderOptions | null;
let matchedType: vscode.TextEditorDecorationType;
let bracketPairs: Map<string, Position> = new Map();
let rainbowTimer: NodeJS.Timer | undefined;
if (is_zil(activeEditor)) {
reloadConfig();
}
vscode.window.onDidChangeActiveTextEditor((editor) => {
activeEditor = editor;
if (is_zil(editor)) {
scheduleRainbowBrackets();
}
}, null, context.subscriptions);
vscode.window.onDidChangeTextEditorSelection((event) => {
if (event.textEditor === vscode.window.activeTextEditor && is_zil(event.textEditor)) {
matchPairs();
}
}, null, context.subscriptions);
vscode.workspace.onDidChangeTextDocument((event) => {
if (is_zil(activeEditor) && event.document === activeEditor.document) {
scheduleRainbowBrackets();
}
}, null, context.subscriptions);
vscode.workspace.onDidChangeConfiguration((event) => {
reloadConfig();
scheduleRainbowBrackets();
}, null, context.subscriptions);
function decorationType(opts: vscode.DecorationRenderOptions) {
opts.rangeBehavior = vscode.DecorationRangeBehavior.ClosedClosed;
return vscode.window.createTextEditorDecorationType(opts);
}
function colorDecorationType(color: BracketColor) {
if (isArray(color)) {
return decorationType({ light: { color: color[0] }, dark: { color: color[1] } });
} else {
return decorationType({ color });
}
}
function reloadConfig() {
if (activeEditor) {
const configuration = vscode.workspace.getConfiguration("zil", activeEditor.document.uri);
let dirty = false;
const enabled = configuration.get<boolean>("rainbowBrackets.enabled");
const configuredColors = enabled ? configuration.get<BracketColor[]>("rainbowBrackets.bracketColors")! : [];
if (!isEqual(rainbowColors, configuredColors)) {
if (rainbowTypes) { rainbowTypes.forEach((type) => type.dispose()); }
rainbowColors = configuredColors;
rainbowTypes = rainbowColors.map(colorDecorationType);
dirty = true;
}
if (cycleBracketColors !== configuration.get<boolean>("rainbowBrackets.cycleBracketColors")) {
cycleBracketColors = configuration.get("rainbowBrackets.cycleBracketColors", false);
dirty = true;
}
if (!isEqual(misplacedBracketStyle, configuration.get("misplacedBracketStyle"))) {
if (misplacedType) { misplacedType.dispose(); }
misplacedBracketStyle = configuration.get<vscode.DecorationRenderOptions>("misplacedBracketStyle")!;
misplacedType = decorationType(misplacedBracketStyle);
dirty = true;
}
if (!isEqual(matchedBracketStyle, configuration.get("matchedBracketStyle"))) {
if (matchedType) { matchedType.dispose(); }
matchedBracketStyle = configuration.get<vscode.DecorationRenderOptions>("matchedBracketStyle")!;
matchedType = decorationType(matchedBracketStyle);
dirty = true;
}
if (dirty) {
scheduleRainbowBrackets();
}
}
}
function scheduleRainbowBrackets() {
if (rainbowTimer) {
clearTimeout(rainbowTimer);
}
if (is_zil(activeEditor)) {
rainbowTimer = setTimeout(updateRainbowBrackets, 16);
}
}
function updateRainbowBrackets() {
if (!is_zil(activeEditor) || !rainbowTypes.length) { return; }
const regexp = /(\\.|;\s*|<\s*>|[ \t-\r,#\':%\(\)\[\]<>\{\}"])/gs;
const doc = activeEditor.document;
const text = doc.getText();
const enabled = vscode.workspace.getConfiguration("zil", doc.uri).get<boolean>("rainbowBrackets.enabled")!;
const rainbow: vscode.DecorationOptions[][] = rainbowTypes.map(() => []);
const misplaced: vscode.DecorationOptions[] = [];
const len = rainbowTypes.length;
const colorIndex: (n: number) => number = cycleBracketColors ? ((i) => i % len) : ((i) => Math.min(i, len - 1));
let match: RegExpExecArray | null;
let in_string = false;
let in_comment = false;
const comment_start_depth: number[] = [];
const stack: Array<{ char: OpenBracket | CloseBracket; pos: Position; pair_idx?: number }> = [];
let stack_depth = 0;
bracketPairs = new Map();
// tslint:disable-next-line:no-conditional-assignment
while (match = regexp.exec(text)) {
const char = match[0];
if (char[0] === "\\") {
continue;
} else if (in_string) {
if (char === "\"") {
in_string = false;
if (in_comment && comment_start_depth[0] === stack_depth) {
comment_start_depth.shift();
in_comment = comment_start_depth.length > 0;
}
continue;
}
} else if (char[0] === ";") {
in_comment = true;
comment_start_depth.unshift(stack_depth);
continue;
} else if (char === "\"") {
in_string = true;
continue;
} else if (opening(char)) {
const pos = activeEditor.document.positionAt(match.index);
if (enabled && !in_comment) {
const decoration = { range: new Range(pos, pos.translate(0, 1)) };
rainbow[colorIndex(stack_depth)].push(decoration);
}
++stack_depth;
stack.push({ char, pos, pair_idx: undefined });
continue;
} else if (closing(char)) {
const pos = activeEditor.document.positionAt(match.index);
const decoration = { range: new Range(pos, pos.translate(0, 1)) };
let pair_idx = stack.length - 1;
while (pair_idx >= 0 && stack[pair_idx].pair_idx !== undefined) {
pair_idx = stack[pair_idx].pair_idx! - 1;
}
if (pair_idx === undefined || pair_idx < 0 || stack[pair_idx].char !== pairs[char]) {
// TODO: only do this if `enabled`
// color misplaced brackets even if in a comment
misplaced.push(decoration);
} else {
const pair = stack[pair_idx];
stack.push({ char, pos, pair_idx });
bracketPairs.set(position_str(pos), pair.pos);
bracketPairs.set(position_str(pair.pos), pos);
--stack_depth;
if (enabled) {
rainbow[colorIndex(stack_depth)].push(decoration);
}
if (in_comment && comment_start_depth[0] >= stack_depth) {
comment_start_depth.shift();
in_comment = comment_start_depth.length > 0;
}
}
continue;
} else {
// atom separator
if (in_comment && comment_start_depth[0] === stack_depth) {
comment_start_depth.shift();
in_comment = comment_start_depth.length > 0;
}
continue;
}
}
for (let i = 0; i < rainbowTypes.length; ++i) {
activeEditor.setDecorations(rainbowTypes[i], rainbow[i]);
}
activeEditor.setDecorations(misplacedType, misplaced);
matchPairs();
}
function matchBefore(doc: vscode.TextDocument, cursor: Position): Position | undefined {
if (cursor.character > 0) {
const cursor_before = cursor.translate(0, -1);
const range_before = new Range(cursor_before, cursor);
const char_before = doc.getText(range_before);
if (closing(char_before)/* || !opening(char_after)*/) {
return bracketPairs.get(position_str(cursor_before));
}
}
}
function matchAfter(doc: vscode.TextDocument, cursor: Position): Position | undefined {
const cursor_after = cursor.translate(0, 1);
if (cursor_after.line === cursor.line) {
const range_after = new Range(cursor, cursor_after);
const char_after = doc.getText(range_after);
if (opening(char_after)/* || !closing(char_before)*/) {
return bracketPairs.get(position_str(cursor));
}
}
}
function matchPairs() {
if (!is_zil(activeEditor)) { return; }
const matches: vscode.DecorationOptions[] = [];
const doc = activeEditor.document;
activeEditor.selections.forEach((selection) => {
const cursor = selection.active;
const match_before = cursor.isBeforeOrEqual(selection.anchor) && matchBefore(doc, cursor);
const match_after = cursor.isAfterOrEqual(selection.anchor) && matchAfter(doc, cursor);
if (match_before) {
matches.push({ range: new Range(cursor.translate(0, -1), cursor) });
matches.push({ range: new Range(match_before, match_before.translate(0, 1)) });
}
if (match_after) {
matches.push({ range: new Range(cursor, cursor.translate(0, 1)) });
matches.push({ range: new Range(match_after, match_after.translate(0, 1)) });
}
});
activeEditor.setDecorations(matchedType, matches);
}
function jumpToMatchingBracket() {
if (!is_zil(activeEditor)) { return; }
const doc = activeEditor.document;
activeEditor.selections = activeEditor.selections.map((selection) => {
const cursor = selection.active;
const match_before = matchBefore(doc, cursor);
const match_after = matchAfter(doc, cursor);
if (match_before) {
return new Selection(match_before, match_before);
} else if (match_after) {
return new Selection(match_after.translate(0, 1), match_after.translate(0, 1));
} else {
return selection;
}
});
activeEditor.revealRange(activeEditor.selections[0]);
}
function selectToMatchingBracket() {
if (!is_zil(activeEditor)) { return; }
const doc = activeEditor.document;
activeEditor.selections = activeEditor.selections.map((selection) => {
const cursor = selection.active;
const match_before = matchBefore(doc, cursor);
const match_after = matchAfter(doc, cursor);
if (match_before) {
return new Selection(cursor, match_before);
} else if (match_after) {
return new Selection(cursor, match_after.translate(0, 1));
} else {
return selection;
}
});
activeEditor.revealRange(new Range(activeEditor.selections[0].active, activeEditor.selections[0].active));
}
}
......@@ -9,7 +9,6 @@ import { Disposable, ExtensionContext } from "vscode";
import { isArray } from "util";
import { startWorkspace } from "./analysis/workspace/workspace";
import startBrackets from "./brackets";
import registerBuildTasks from "./providers/buildTasks";
import registerCompletionProvider from "./providers/completion";
import registerDebugConfigProvider from "./providers/debugConfig";
......@@ -30,7 +29,6 @@ export async function activate(context: ExtensionContext) {
registerREPL,
registerSignatureProvider,
registerSymbolProvider,
startBrackets,
startWorkspace,
];
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment