From 71546723aba1e47f8dcc5646ce14ea57365dd6c7 Mon Sep 17 00:00:00 2001 From: Paulo Valente Date: Mon, 5 Aug 2019 02:16:09 -0300 Subject: [PATCH] feat: implement ignore blocks to allow for proper commented code and string text ignoring and improve elixir syntax matching feat: support other languages in ignore blocks --- CHANGELOG.md | 38 +++-- package-lock.json | 2 +- package.json | 2 +- src/extension.ts | 43 ++++- src/languages.ts | 424 +++++++++++++++++++++++++--------------------- 5 files changed, 292 insertions(+), 217 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6a7617..4679ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,41 +1,51 @@ +## 0.7.0 + +By https://github.com/polvalente + +- Improve Elixir support +- Add code block ignore functionality + - Add said ignore blocks for some of the supported languages +- Allow matching for the first character in the document + ## 0.6.0 By https://github.com/spgennard: -* Support for case insensitive language -* Cobol support + +- Support for case insensitive language +- Cobol support ## 0.5.4 -* Fixes for Verilog +- Fixes for Verilog ## 0.5.3 -* Fixes for VHDL +- Fixes for VHDL ## 0.5.2 -* Fix do-end for crystal +- Fix do-end for crystal ## 0.5.1 -* Add loop keyword for ruby -* Add with and quote keywords for Elixir (#4) +- Add loop keyword for ruby +- Add with and quote keywords for Elixir (#4) ## 0.5.0 -* Verilog support +- Verilog support ## 0.4.0 -* Upgrade dependencies -* Add a dedicated changelog -* Add support for the ruby keyword unless -* Ruby inline if & unless support (Fix #2) +- Upgrade dependencies +- Add a dedicated changelog +- Add support for the ruby keyword unless +- Ruby inline if & unless support (Fix #2) ### 0.3.0 -* Add support for Crystal language -* Add support for Shell language +- Add support for Crystal language +- Add support for Shell language ### 0.2.0 diff --git a/package-lock.json b/package-lock.json index 8515fe5..8a9264c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rainbow-end", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 578b195..c379132 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "Apache-2.0", "displayName": "Rainbow End", "description": "This extension allows to identify keyword / end with colours.", - "version": "0.6.0", + "version": "0.7.0", "icon": "images/logo.png", "engines": { "vscode": "^1.29.0" diff --git a/src/extension.ts b/src/extension.ts index a255aff..7e40133 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,23 +1,23 @@ 'use strict'; import * as vscode from 'vscode'; -import {languages} from './languages'; +import { languages } from './languages'; const deepDecorations = [ vscode.window.createTextEditorDecorationType({ - color: {id: "rainbowend.deep1"} + color: { id: "rainbowend.deep1" } }), vscode.window.createTextEditorDecorationType({ - color: {id: "rainbowend.deep2"} + color: { id: "rainbowend.deep2" } }), vscode.window.createTextEditorDecorationType({ - color: {id: "rainbowend.deep3"} + color: { id: "rainbowend.deep3" } }) ]; -let timeout : NodeJS.Timer | null = null; -let regExs: { [index:string] : RegExp} = {}; +let timeout: NodeJS.Timer | null = null; +let regExs: { [index: string]: RegExp } = {}; export function activate(context: vscode.ExtensionContext) { Object.keys(languages).forEach(language => { @@ -51,6 +51,7 @@ function triggerUpdateDecorations(activeEditor: vscode.TextEditor) { } function buildRegex(language: string) { + const languageConfiguration = languages[language]; let tokens: Array = languageConfiguration["openTokens"]; tokens = tokens.concat(languageConfiguration["inlineOpenTokens"]); @@ -59,6 +60,24 @@ function buildRegex(language: string) { return RegExp("(\\b)(" + tokens.join('|') + ")(\\b)", "gm"); } +function ignoreInDelimiters(token_pairs: Array<{ + open: string, + close: string +}> | undefined, text: string) { + if (token_pairs) { + token_pairs.forEach(({ + open: open_delim, + close: close_delim + }) => { + let regexp = RegExp(`${open_delim}[^${close_delim}]*${close_delim}`, "gm"); + text = text.replace(regexp, (match) => { + return " ".repeat(match.length); + }); + }) + } + return text; +} + function updateDecorations() { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor) { @@ -78,11 +97,19 @@ function updateDecorations() { if (!languageConfiguration.caseSensitive) { text = text.toLowerCase(); } + // substitute all ignore intervals with spaces + // this ensures commented code or + // keywords inside strings are ignored properly + + // also, prepend a whitespace to allow matching the first character in document + // if needed + + text = ' ' + ignoreInDelimiters(languageConfiguration.ignoreInDelimiters, text); while (match = regExs[activeEditor.document.languageId].exec(text)) { - const startIndex = match.index + match[1].length; + const startIndex = match.index + match[1].length - 1; // Decrement to compensate for added character const startPos = activeEditor.document.positionAt(startIndex); const endPos = activeEditor.document.positionAt(startIndex + match[2].length); - const decoration: vscode.DecorationOptions = { range: new vscode.Range(startPos, endPos) }; + const decoration: vscode.DecorationOptions = { range: new vscode.Range(startPos, endPos) }; if (languageConfiguration.closeTokens.indexOf(match[2]) > -1) { if (deep > 0) { diff --git a/src/languages.ts b/src/languages.ts index b47acad..185d676 100644 --- a/src/languages.ts +++ b/src/languages.ts @@ -1,196 +1,234 @@ export const languages: { - [index: string]: { - caseSensitive: boolean, - inlineOpenTokens: Array, - openTokens: Array, - closeTokens: Array, - neutralTokens: Array - } + [index: string]: { + caseSensitive: boolean; + ignoreInDelimiters?: Array<{ + open: string; + close: string; + }>; + inlineOpenTokens: Array; + openTokens: Array; + closeTokens: Array; + neutralTokens: Array; + }; } = { - ruby: { - caseSensitive: true, - inlineOpenTokens: [ // Allow stuff like return toto if tutu - "if", - "unless", - ], - openTokens: [ - "class", - "module", - "def", - "while", - "do", - "case", - "begin", - "loop", - ], - closeTokens: [ - "end", - ], - neutralTokens: [ - "elsif", - "else", - "when", - "rescue" - ] - }, - lua: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "function", - "if", - "while", - "for" - ], - closeTokens: [ - "end", - ], - neutralTokens: [ - "then", - "else", - "elseif", - ] - }, - elixir: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "defmodule", - "defmacro", - "def", - "if", - "while", - "for", - "case", - "cond", - "unless", - "try", - "quote", - "with", - ], - closeTokens: [ - "end", - ], - neutralTokens: [ - "do", - "else", - "elseif", - "rescue", - "after" - ] - }, - shellscript: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "for", - "if", - "while", - "until" - ], - closeTokens: [ - "fi", - "done" - ], - neutralTokens: [ - "do", - "in", - "then", - "else" - ] - }, - verilog: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "module", - "case", - "begin" - ], - closeTokens: [ - "end", - "endmodule", - "endcase" - ], - neutralTokens: [ - ] - }, - vhdl: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "entity", - "component", - "case", - "begin" - ], - closeTokens: [ - "end", - "endcase" - ], - neutralTokens: [ - ] - }, - crystal: { - caseSensitive: true, - inlineOpenTokens: [], - openTokens: [ - "class", - "module", - "struct", - "enum", - "macro", - "def", - "if", - "while", - "case", - "unless", - "until", - "try", - "do", - ], - closeTokens: [ - "end", - ], - neutralTokens: [ - "else", - "elseif", - "rescue", - "ensure" - ] - }, - COBOL: { - caseSensitive: false, - inlineOpenTokens: [], - openTokens: [ - "program-id", - "perform", - "evalute", - "read", - "perform", - "call", - "evaluate", - "if", - "method-id" - ], - closeTokens: [ - "end-perform", - "end-evalute", - "end-read", - "end-perform", - "end-call", - "end-evaluate", - "end-if", - "end program", - "end method" - ], - neutralTokens: [ - "entry", - "else", - "when", - "procedure division", - "goback", - "exit program" - ] - }, + ruby: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: "#", + close: "\n" + }, + { open: "=begin", close: "=end" }, + { + open: '"', + close: '"' + } + ], + inlineOpenTokens: [ + // Allow stuff like return toto if tutu + "if", + "unless" + ], + openTokens: [ + "class", + "module", + "def", + "while", + "do", + "case", + "begin", + "loop" + ], + closeTokens: ["end"], + neutralTokens: ["elsif", "else", "when", "rescue"] + }, + lua: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: '"', + close: '"' + }, + { + open: "'", + close: "'" + }, + { + open: "--\\[\\[", + close: "--\\]\\]" + }, + { + open: "--", + close: "\n" + } + ], + inlineOpenTokens: [], + openTokens: ["function", "if", "while", "for"], + closeTokens: ["end"], + neutralTokens: ["then", "else", "elseif"] + }, + elixir: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: "#", + close: "\n" + }, + { + open: '"""', + close: '"""' + }, + { + open: '"', + close: '"' + }, + { + open: "'", + close: "'" + } + ], + inlineOpenTokens: [], + openTokens: [ + "fn", + "defmodule", + "defmacro(?=.+do)", + "defmacrop(?=.+do)", + "def(?=.+do)", + "defp(?=.+do)", + "if", + "while", + "for", + "case", + "cond", + "unless", + "try", + "quote", + "with", + "defprotocol", + "defimpl", + "schema", + "embedded_schema", + "resources(?=.+do)", + "scope(?=.+do)" + ], + closeTokens: ["end", ", do"], + neutralTokens: ["do", "else", "elseif", "rescue", "after"] + }, + shellscript: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: "#", + close: "\n" + }, + { + open: '"', + close: '"' + }, + { + open: "'", + close: "'" + } + ], + inlineOpenTokens: [], + openTokens: ["for", "if", "while", "until"], + closeTokens: ["fi", "done"], + neutralTokens: ["do", "in", "then", "else"] + }, + verilog: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: "/\\*", + close: "\\*/" + }, + { + open: "//", + close: "\n" + } + ], + inlineOpenTokens: [], + openTokens: ["module", "case", "begin"], + closeTokens: ["end", "endmodule", "endcase"], + neutralTokens: [] + }, + vhdl: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: "--", + close: "\n" + } + ], + inlineOpenTokens: [], + openTokens: ["entity", "component", "case", "begin"], + closeTokens: ["end", "endcase"], + neutralTokens: [] + }, + crystal: { + caseSensitive: true, + ignoreInDelimiters: [ + { + open: '"', + close: '"' + }, + { + open: "#", + close: "\n" + } + ], + + inlineOpenTokens: [], + openTokens: [ + "class", + "module", + "struct", + "enum", + "macro", + "def", + "if", + "while", + "case", + "unless", + "until", + "try", + "do" + ], + closeTokens: ["end"], + neutralTokens: ["else", "elseif", "rescue", "ensure"] + }, + COBOL: { + caseSensitive: false, + inlineOpenTokens: [], + openTokens: [ + "program-id", + "perform", + "evalute", + "read", + "perform", + "call", + "evaluate", + "if", + "method-id" + ], + closeTokens: [ + "end-perform", + "end-evalute", + "end-read", + "end-perform", + "end-call", + "end-evaluate", + "end-if", + "end program", + "end method" + ], + neutralTokens: [ + "entry", + "else", + "when", + "procedure division", + "goback", + "exit program" + ] + } };