├── samples ├── vhdl.vhdl ├── asp.asp ├── razor.cshtml ├── plsql.plsql ├── .vscode │ └── settings.json ├── xml.xml ├── css.css ├── plantuml.pu ├── shellscript.sh ├── asciidoc.adoc ├── smarty.smarty ├── sass.sass ├── jsonc.jsonc ├── scss.scss ├── lua.lua ├── stylus.styl ├── markdown.md ├── puppet.pp ├── ruby.rb ├── brightscript.brs ├── plaintext.txt ├── cfc.cfc ├── yaml.yaml ├── coldfusiontags.cfm ├── matlab.m ├── fortran.f03 ├── graphql.graphql ├── groovy.groovy ├── solidity.sol ├── twig.twig ├── elixer.ex ├── d.d ├── php.php ├── apex.cls ├── ejs.ejs ├── freemarker.ftl ├── bibreference.bib ├── sas.sas ├── powershell.ps1 ├── hive.q ├── terraform.tf ├── gdscript.gd ├── haskell.hs ├── javascript.js ├── html.html ├── vala.vala ├── svelte.svelte ├── genstat.gen ├── stata.do ├── elm.elm ├── csharp.cs ├── tcl.tcl ├── pascal.pas ├── shaderlab.shader ├── coldfusion.cfc ├── pig.pig ├── verilog.v ├── typescriptreact.tsx ├── dart.dart ├── python.py ├── vuejs.vue ├── typescript.ts ├── cobol.cbl ├── django.html ├── modernpascal.pp ├── AL.al ├── racket.rkt ├── nim.nim ├── nested.cs ├── lisp.lisp └── clojure.clj ├── src ├── log │ ├── index.ts │ └── console.ts ├── definition │ ├── index.ts │ ├── modules │ │ ├── php.ts │ │ ├── index.ts │ │ └── common.ts │ ├── event.ts │ └── definition.ts ├── configuration │ ├── index.ts │ ├── event.ts │ └── configuration.ts ├── handler │ ├── index.ts │ ├── modules │ │ ├── plaintext.ts │ │ ├── shellscript.ts │ │ ├── react.ts │ │ └── common.ts │ ├── handler.ts │ └── event.ts ├── utils │ ├── regex.ts │ ├── utils.ts │ ├── promise.ts │ └── str.ts └── extension.ts ├── pnpm-workspace.yaml ├── .gitignore ├── .prettierrc ├── .npmrc ├── .github ├── FUNDING.yml └── workflows │ └── release.yml ├── static ├── icon.png ├── icon_large.png ├── strict_false.png ├── strict_true.png └── better-comments.png ├── .husky └── pre-commit ├── .vscodeignore ├── .vscode ├── extensions.json ├── settings.json ├── tasks.json └── launch.json ├── .vscode-test.mjs ├── lint-staged.config.mjs ├── .editorconfig ├── .travis.yml ├── tsconfig.json ├── eslint.config.mjs ├── LICENSE.md ├── README.md ├── CHANGELOG.md └── package.json /samples/vhdl.vhdl: -------------------------------------------------------------------------------- 1 | -- ! comment -------------------------------------------------------------------------------- /src/log/index.ts: -------------------------------------------------------------------------------- 1 | export * from './console'; 2 | -------------------------------------------------------------------------------- /samples/asp.asp: -------------------------------------------------------------------------------- 1 | ' ! comment 2 | 3 | ' * hello world -------------------------------------------------------------------------------- /samples/razor.cshtml: -------------------------------------------------------------------------------- 1 | @* ! here is the code to comment *@ -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - esbuild 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | *.vsix 5 | .eslintcache 6 | -------------------------------------------------------------------------------- /samples/plsql.plsql: -------------------------------------------------------------------------------- 1 | -- ! comment 2 | 3 | /* 4 | ! hello world 5 | */ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | node-linker=hoisted 3 | public-hoist-pattern=* 4 | -------------------------------------------------------------------------------- /samples/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "better-comments.fullHighlight": true 3 | } -------------------------------------------------------------------------------- /samples/xml.xml: -------------------------------------------------------------------------------- 1 | abc 2 | 3 | -------------------------------------------------------------------------------- /src/definition/index.ts: -------------------------------------------------------------------------------- 1 | export * from './definition'; 2 | export * from './event'; 3 | -------------------------------------------------------------------------------- /src/configuration/index.ts: -------------------------------------------------------------------------------- 1 | export * from './configuration'; 2 | export * from './event'; 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: edwinhuish 4 | -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwinhuish/better-comments-next/HEAD/static/icon.png -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /static/icon_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwinhuish/better-comments-next/HEAD/static/icon_large.png -------------------------------------------------------------------------------- /static/strict_false.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwinhuish/better-comments-next/HEAD/static/strict_false.png -------------------------------------------------------------------------------- /static/strict_true.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwinhuish/better-comments-next/HEAD/static/strict_true.png -------------------------------------------------------------------------------- /static/better-comments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edwinhuish/better-comments-next/HEAD/static/better-comments.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | **/* 2 | !node_modules 3 | !dist 4 | !static 5 | !CHANGELOG.md 6 | !LICENSE.md 7 | !package.json 8 | !README.md 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "luxass.tsup-problem-matchers" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /samples/css.css: -------------------------------------------------------------------------------- 1 | .myclass { 2 | /* 3 | ! this is a comment 4 | */ 5 | 6 | /* ! hello */ 7 | height: 100px; 8 | width: 100px; 9 | } -------------------------------------------------------------------------------- /.vscode-test.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@vscode/test-cli'; 2 | 3 | export default defineConfig({ 4 | files: 'out/test/**/*.test.js', 5 | }); 6 | -------------------------------------------------------------------------------- /samples/plantuml.pu: -------------------------------------------------------------------------------- 1 | @startuml 2 | ' This is a comment on a single line 3 | Bob->Alice : hello 4 | ' ! You quote alors use slash-and-quote 5 | ' * hello world 6 | @enduml -------------------------------------------------------------------------------- /samples/shellscript.sh: -------------------------------------------------------------------------------- 1 | # ! This is a comment! 2 | echo Hello World # This is a comment, too! 3 | 4 | # Set logfile 5 | # shellcheck disable=SC2034 6 | logfile="$logDir/${0##*/}.log" -------------------------------------------------------------------------------- /samples/asciidoc.adoc: -------------------------------------------------------------------------------- 1 | This is asciidoc 2 | = This does something 3 | 4 | // ! comment 5 | 6 | //// 7 | 8 | Block comment 9 | ! hello world 10 | * this is a comment 11 | 12 | //// -------------------------------------------------------------------------------- /lint-staged.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | '*.{js,jsx,ts,tsx,vue}': () => { 3 | return [ 4 | 'tsc -p . --noEmit', 5 | 'eslint --cache --max-warnings 0 ', 6 | ]; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /samples/smarty.smarty: -------------------------------------------------------------------------------- 1 | This snippet: 2 | 3 | {clear} 4 | 5 | will write this to your document: 6 | 7 |
8 | 9 | {* TODO: this should be done *} 10 | 11 | {* !: alert *} -------------------------------------------------------------------------------- /samples/sass.sass: -------------------------------------------------------------------------------- 1 | $font-stack: Helvetica, sans-serif 2 | $primary-color: #333 3 | 4 | body 5 | font: 100% $font-stack 6 | color: $primary-color 7 | // ! hello world 8 | 9 | /* 10 | ! hello world 11 | */ -------------------------------------------------------------------------------- /samples/jsonc.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | // This is a comment in JSONC 3 | // ! this one is colored 4 | "item1": 1, 5 | "item2": { 6 | "child": "" 7 | } 8 | 9 | /* 10 | * some comments 11 | */ 12 | } -------------------------------------------------------------------------------- /samples/scss.scss: -------------------------------------------------------------------------------- 1 | .topClass { 2 | 3 | height: 100px; 4 | width: 100px; 5 | 6 | /* 7 | ! hello 8 | */ 9 | 10 | // ! This class is nested 11 | .innerClass { 12 | color: pink; 13 | } 14 | } -------------------------------------------------------------------------------- /samples/lua.lua: -------------------------------------------------------------------------------- 1 | function entry0 (o) 2 | N=N + 1 3 | local title = o.title or '(no title)' 4 | fwrite('
  • %s\n', N, title) 5 | end 6 | -- ! some comments 7 | 8 | --[[ 9 | ! hello world 10 | --]] -------------------------------------------------------------------------------- /samples/stylus.styl: -------------------------------------------------------------------------------- 1 | .topClass { 2 | 3 | height: 100px; 4 | width: 100px; 5 | 6 | /** 7 | * ! hello 8 | */ 9 | 10 | // ! This class is nested 11 | .innerClass { 12 | color: pink; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/markdown.md: -------------------------------------------------------------------------------- 1 | ### some markdown 2 | ## Some other header 3 | 4 | * bullet point 5 | * bullet point 2 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /samples/puppet.pp: -------------------------------------------------------------------------------- 1 | # This is a simple smoke test 2 | # of the file_line resource type. 3 | # ! alert 4 | # ? question 5 | file { '/tmp/dansfile': 6 | ensure => file, 7 | } 8 | -> file_line { 'dans_line': 9 | line => 'dan is awesome', 10 | path => '/tmp/dansfile', 11 | } 12 | -------------------------------------------------------------------------------- /samples/ruby.rb: -------------------------------------------------------------------------------- 1 | # ! this is a alert 2 | def say_hello 3 | # # print to stdout 4 | puts "Hello, World!" 5 | end 6 | 7 | # # call function 8 | say_hello 9 | 10 | # 11 | # todo: multi-line in block comment example 12 | # ensure continuous lines with indentation. 13 | # 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | max_line_length = 120 12 | 13 | [*.json] 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /samples/brightscript.brs: -------------------------------------------------------------------------------- 1 | #const FeatureA = true 2 | #const FeatureB = false 3 | 4 | #if FeatureA 5 | 6 | 'code for Feature A 7 | ' ! alert 8 | 9 | #else if FeatureB 10 | 11 | 'code for Feature B 12 | ' ? question 13 | 14 | #else 15 | 16 | 'production code 17 | 18 | #end if -------------------------------------------------------------------------------- /samples/plaintext.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | this is some plain text 3 | and here is a comment of my own design 4 | 5 | ! my comment 6 | ? comment 7 | 8 | todo: multiline comment 9 | can have multiple lines 10 | ensure continuous lines with indentation. 11 | 12 | This is some more text 13 | -------------------------------------------------------------------------------- /samples/cfc.cfc: -------------------------------------------------------------------------------- 1 | 2 | 5 | No, it didn't 6 | 7 | 8 | 9 | No, it didn't 10 | 11 | 12 | 13 | No, it didn't 14 | -------------------------------------------------------------------------------- /samples/yaml.yaml: -------------------------------------------------------------------------------- 1 | # ! Employee records 2 | - martin: 3 | name: Martin D'vloper 4 | job: Developer 5 | skills: 6 | - python 7 | - perl 8 | - pascal 9 | - tabitha: 10 | name: Tabitha Bitumen 11 | job: Developer 12 | skills: 13 | - lisp 14 | - fortran 15 | - erlang -------------------------------------------------------------------------------- /samples/coldfusiontags.cfm: -------------------------------------------------------------------------------- 1 | 2 | 5 | No, it didn't 6 | 7 | 8 | 9 | No, it didn't 10 | 11 | 12 | 13 | No, it didn't 14 | -------------------------------------------------------------------------------- /samples/matlab.m: -------------------------------------------------------------------------------- 1 | function output = my_fun(input) 2 | % 3 | % description. 4 | % 5 | % @since 1.0.0 6 | % @param {type} [name] description. 7 | % @return {type} [name] description. 8 | % @see dependencies 9 | % ! hello world 10 | % * another comment 11 | % 12 | 13 | 14 | for index = 1 : n 15 | 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /samples/fortran.f03: -------------------------------------------------------------------------------- 1 | program circle 2 | real r, area 3 | 4 | c ! This program reads a real number r and prints 5 | c * the area of a circle with radius r. 6 | 7 | write (*,*) 'Give radius r:' 8 | read (*,*) r 9 | area = 3.14159*r*r 10 | write (*,*) 'Area = ', area 11 | 12 | stop 13 | end 14 | ! * abc -------------------------------------------------------------------------------- /samples/graphql.graphql: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "hero": { 4 | "name": "R2-D2", 5 | "friends": [ 6 | { 7 | "name": "Luke Skywalker" 8 | }, 9 | { 10 | "name": "Han Solo" 11 | }, 12 | { 13 | "name": "Leia Organa" 14 | } 15 | ] 16 | } 17 | } 18 | } 19 | # ! hello -------------------------------------------------------------------------------- /samples/groovy.groovy: -------------------------------------------------------------------------------- 1 | class Example { 2 | static void main(String[] args) { 3 | // ! hello world 4 | // * another comment 5 | // ? and another 6 | One can see the use of a semi-colon after each statement 7 | def x = 5; 8 | println('Hello World'); 9 | } 10 | } 11 | 12 | /* 13 | ! block comments are the same 14 | */ -------------------------------------------------------------------------------- /src/definition/modules/php.ts: -------------------------------------------------------------------------------- 1 | import type { AvailableComments } from './common'; 2 | import { Language } from './common'; 3 | 4 | export class PHPLanguage extends Language { 5 | public setAvailableComments(comments: AvailableComments): this { 6 | comments.lineComments.push('#'); 7 | 8 | return super.setAvailableComments(comments); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/solidity.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // compiler version must be greater than or equal to 0.8.10 and less than 0.9.0 3 | pragma solidity ^0.8.10; 4 | 5 | contract HelloWorld { 6 | string public greet = "Hello World!"; 7 | } 8 | 9 | // ! hello world 10 | 11 | /* 12 | ! solidity 13 | */ 14 | 15 | /** 16 | * ! hello world 17 | */ -------------------------------------------------------------------------------- /samples/twig.twig: -------------------------------------------------------------------------------- 1 | {% if cons.inConsGroup(1) %} 2 | Thank you for your volunteer efforts thus far! 3 | {% else %} 4 | According to our records associated with this email address, you haven't volunteered. 5 | {% endif %} 6 | {% if cons.inConsGroup(2) %} 7 | Please setup a recurring gift today: 8 | {% endif %} 9 | 10 | {# 11 | ! This is a comment 12 | #} -------------------------------------------------------------------------------- /samples/elixer.ex: -------------------------------------------------------------------------------- 1 | @moduledoc 2 | """" 3 | Some description 4 | ! ALERT 5 | """ 6 | 7 | ["T", "e", "s", "t"] = String.graphemes("Test") 8 | 9 | # Contrast this with codepoints which may return 10 | # multiple codepoints for a single character 11 | # ! alert 12 | # ? question 13 | # TODO 14 | ["ö"] = String.graphemes("ö") 15 | ["o", "̈"] = String.codepoints("ö") -------------------------------------------------------------------------------- /samples/d.d: -------------------------------------------------------------------------------- 1 | //version(none) // ! this is the original version line 2 | version(all) // ? the code is now enabled 3 | void brokenFunc() { 4 | 5 | /* 6 | 7 | ! comment freely here 8 | 9 | */ 10 | 11 | /** 12 | * * abc 13 | */ 14 | 15 | int x=123; 16 | int y=321; /+ to your heart's content +/ 17 | } 18 | 19 | void workingFunc() { 20 | 21 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Disable the default formatter, use eslint instead 4 | "prettier.enable": false, 5 | "editor.formatOnSave": false, 6 | // Auto fix 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll.eslint": "explicit", 9 | "source.organizeImports": "never" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/php.php: -------------------------------------------------------------------------------- 1 | { 9 | for (const editor of vscode.window.visibleTextEditors) { 10 | handler.triggerUpdateDecorations({ editor }); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /samples/ejs.ejs: -------------------------------------------------------------------------------- 1 | <% if (user) { %> 2 |

    <%= user.name %>

    3 | <% } %> 4 | 5 | 6 | 7 | 8 | let template = ejs.compile(str, options); 9 | template(data); 10 | // => Rendered HTML string 11 | 12 | ejs.render(str, data, options); 13 | // => Rendered HTML string 14 | 15 | ejs.renderFile(filename, data, options, function(err, str){ 16 | // str => Rendered HTML string 17 | }); -------------------------------------------------------------------------------- /samples/freemarker.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | Welcome! 4 | 5 | 6 | <#-- Greet the user with his/her name --> 7 |

    Welcome ${user}!

    8 |

    We have these animals: 9 |

    14 | 15 | 16 | 17 | <#-- ! Greet the user with his/her name --> -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: linux 3 | language: node_js 4 | node_js: 8.9.2 5 | 6 | services: 7 | - xvfb 8 | 9 | before_install: 10 | - export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0; 11 | 12 | install: 13 | - npm install 14 | - npm run vscode:prepublish 15 | 16 | script: 17 | - npm test --silent 18 | 19 | after_failure: 20 | - cat /home/travis/build/edwinhuish/better-comments/npm-debug.log 21 | -------------------------------------------------------------------------------- /samples/bibreference.bib: -------------------------------------------------------------------------------- 1 | % This is a Bibtex MWE 2 | % ? Question 3 | % ! Warning 4 | % * Comment 5 | % //deprecated 6 | % * Bibtex style 7 | % TODO: Task 8 | @misc{ RefKey1, 9 | author = "Bruce Banner", 10 | title = "Anger management", 11 | year = "2001" 12 | } 13 | % * Biber style 14 | @misc{ RefKey2, 15 | author = {Tony Stark}, 16 | title = {Introduction to Engineering}, 17 | year = {2006} 18 | } -------------------------------------------------------------------------------- /samples/sas.sas: -------------------------------------------------------------------------------- 1 | * ! this is a comment 2 | /* ? also this is */ 3 | 4 | DATA CLASS; 5 | INPUT NAME $ 1-8 SEX $ 10 AGE 12-13 HEIGHT 15-16 WEIGHT 18-22; 6 | CARDS; 7 | JOHN M 12 59 99.5 8 | JAMES M 12 57 83.0 9 | ALFRED M 14 69 112.5 10 | ALICE F 13 56 84.0 11 | 12 | PROC MEANS; 13 | VAR AGE HEIGHT WEIGHT; 14 | PROC PLOT; 15 | PLOT WEIGHT*HEIGHT; 16 | ENDSAS; 17 | ; -------------------------------------------------------------------------------- /samples/powershell.ps1: -------------------------------------------------------------------------------- 1 | # Convert any text file to ASCII 2 | 3 | param( [string] $infile = $(throw "Please specify a filename.") ) 4 | 5 | $outfile = "$infile.ascii" 6 | 7 | get-content -path $infile | out-file $outfile -encoding ascii 8 | 9 | # ! comment here 10 | # ? another comment 11 | # TODO: some other comment 12 | 13 | # * highlighted comment 14 | 15 | <# 16 | ! hello world 17 | * hello world 18 | ? hello world 19 | #> -------------------------------------------------------------------------------- /samples/hive.q: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS docs; 2 | 3 | -- ! this is a comment 4 | CREATE TABLE docs (line STRING); 5 | 6 | -- * some highlighted comment 7 | LOAD DATA INPATH 'input_file' OVERWRITE INTO TABLE docs; 8 | 9 | -- TODO: create schema 10 | CREATE TABLE word_counts AS 11 | 12 | SELECT word, count(1) AS count FROM 13 | 14 | (SELECT explode(split(line, '\s')) AS word FROM docs) temp 15 | 16 | GROUP BY word 17 | 18 | ORDER BY word; -------------------------------------------------------------------------------- /samples/terraform.tf: -------------------------------------------------------------------------------- 1 | # ! An AMI 2 | variable "ami" { 3 | description = "the AMI to use" 4 | } 5 | 6 | /** 7 | * A multi 8 | * ? line comment 9 | */ 10 | resource "aws_instance" "web" { 11 | ami = "${var.ami}" 12 | count = 2 13 | source_dest_check = false 14 | 15 | connection { 16 | user = "root" 17 | } 18 | } 19 | 20 | 21 | 22 | # ! hello 23 | // ! hello 24 | /* 25 | ! hello 26 | */ -------------------------------------------------------------------------------- /src/definition/modules/index.ts: -------------------------------------------------------------------------------- 1 | import type { Language } from './common'; 2 | import { CommonLanguage } from './common'; 3 | import { PHPLanguage } from './php'; 4 | 5 | export * from './common'; 6 | export * from './php'; 7 | 8 | export function useLanguage(langId: string): Language { 9 | switch (langId) { 10 | case 'php': 11 | return new PHPLanguage(langId); 12 | default: 13 | return new CommonLanguage(langId); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/gdscript.gd: -------------------------------------------------------------------------------- 1 | extends StaticBody2D 2 | 3 | # ! class member variables go here 4 | var a = 2 5 | var b = "textvar" 6 | 7 | func _ready(): 8 | # * Called when the node is added to the scene for the first time. 9 | # ? is this loop necessary? 10 | for i in range(20): 11 | print(i) 12 | 13 | func _process(delta): 14 | # TODO: Add logic 15 | # 16 | # Called every frame. Delta is time since last frame. 17 | # Update game logic here. 18 | pass 19 | 20 | -------------------------------------------------------------------------------- /samples/haskell.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | {-# LANGUAGE TypeFamilies #-} 3 | {-# LANGUAGE QuasiQuotes #-} 4 | {-# LANGUAGE TemplateHaskell #-} 5 | {-# LANGUAGE MultiParamTypeClasses #-} 6 | 7 | import Yesod 8 | 9 | data WebApp = WebApp 10 | 11 | instance Yesod WebApp 12 | 13 | mkYesod "WebApp" [parseRoutes| 14 | / HomeR GET 15 | |] 16 | 17 | getHomeR = defaultLayout [whamlet| 18 |
    Hello, world! 19 | |] 20 | 21 | main = warpEnv WebApp 22 | 23 | -- ! hello world 24 | {- 25 | ! hello world 26 | -} -------------------------------------------------------------------------------- /samples/javascript.js: -------------------------------------------------------------------------------- 1 | /** 2 | * My Class 3 | * TODO: Create some copyright notes 4 | */ 5 | 6 | /* 7 | * non js-doc comments 8 | ! non js-doc comments 9 | */ 10 | const myVar = 123; 11 | 12 | /** 13 | * My Function 14 | * ! Some Alert 15 | * TODO: multiline comment example 16 | * ensure continuous lines with indentation. 17 | * 18 | * ? Questions 19 | * * Highlights 20 | */ 21 | function myFunction(myParam) { 22 | return myParam; 23 | } 24 | 25 | // ! Need more content here 26 | 27 | // // removed code 28 | 29 | // * End of the file 30 | -------------------------------------------------------------------------------- /samples/html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    8 | 9 |

    TEST

    10 |
    11 | This text should not be highlighted 12 | 13 | 16 |
    17 | Some content can go here 18 |
    19 | 20 | 21 | -------------------------------------------------------------------------------- /src/log/console.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | const channel = vscode.window.createOutputChannel('Better Comments'); 4 | 5 | function log(...args: any[]) { 6 | const line = args.map(obj => (typeof obj === 'object' ? JSON.stringify(obj) : obj)).join(' '); 7 | channel.appendLine(line); 8 | } 9 | 10 | export function info(...args: any[]) { 11 | log('[INFO]', ...args); 12 | } 13 | 14 | export function error(...args: any[]) { 15 | log('[ERROR]', ...args); 16 | } 17 | 18 | export function warn(...args: any[]) { 19 | log('[WARNING]', ...args); 20 | } 21 | -------------------------------------------------------------------------------- /samples/vala.vala: -------------------------------------------------------------------------------- 1 | using Gtk; 2 | 3 | // ! hello world 4 | 5 | /* 6 | ! hello 7 | */ 8 | 9 | int main (string[] args) { 10 | Gtk.init (ref args); 11 | 12 | var window = new Window (); 13 | window.title = "Hello, World!"; 14 | window.border_width = 10; 15 | window.window_position = WindowPosition.CENTER; 16 | window.set_default_size (350, 70); 17 | window.destroy.connect (Gtk.main_quit); 18 | 19 | var label = new Label ("Hello, World!"); 20 | 21 | window.add (label); 22 | window.show_all (); 23 | 24 | Gtk.main (); 25 | return 0; 26 | } 27 | 28 | /** 29 | * ! hello world 30 | */ -------------------------------------------------------------------------------- /samples/svelte.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /samples/genstat.gen: -------------------------------------------------------------------------------- 1 | print 0 2 | \ Line comment only at the start of a line 3 | \ Line comment only at the start of a line 4 | \ ! Line comment only at the start of a line 5 | \ * Line comment only at the start of a line 6 | print 2 " Block comment at the end of a line " 7 | print 3 " Block comment at the end of a line 8 | ! testing 9 | possibly continuing to the next line 10 | " 11 | print 4 12 | "============================================================================= 13 | Long block comment 14 | ==============================================================================" 15 | print 5 16 | " Another block comment" 17 | print 6 -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "isBackground": true, 10 | "label": "npm: watch", 11 | "problemMatcher": "$tsup-cjs-watch", 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | }, 17 | { 18 | "type": "npm", 19 | "script": "watch-web", 20 | "isBackground": true, 21 | "label": "npm: watch-web", 22 | "problemMatcher": "$tsup-cjs-watch", 23 | "group": "build" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/handler/modules/plaintext.ts: -------------------------------------------------------------------------------- 1 | import type { BlockCommentSlice, PickParams } from './common'; 2 | import * as configuration from '@/configuration'; 3 | import { CommonHandler } from './common'; 4 | 5 | export class PlainTextHandler extends CommonHandler { 6 | protected async pickBlockCommentSlices(params: PickParams): Promise> { 7 | if (!configuration.getConfigurationFlatten().highlightPlainText) { 8 | return []; 9 | } 10 | 11 | return [{ 12 | start: params.offset - 1, 13 | end: params.offset + params.text.length, 14 | comment: `\n${params.text}`, 15 | content: `\n${params.text}`, 16 | marks: ['', ''], 17 | }]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/stata.do: -------------------------------------------------------------------------------- 1 | * ! Stata do-file carsdata.do written January 2009 2 | * ? Create a text log file that stores the results 3 | log using carsdata.txt, text replace 4 | * Read in the Stata data set carsdata.dta 5 | use carsdata.dta 6 | * Describe the variables in the data set 7 | describe 8 | * List the dataset 9 | list 10 | * TODO: Provide summary statistics of the variables in the data set 11 | summarize 12 | * Provide an X,Y scatterplot with a regression line 13 | twoway (scatter cars hhsize) (lfit cars hhsize) 14 | * Save the preceding graph in a file in PNG (portable networks graphic) format 15 | graph export carsdata.png 16 | * Regress cars on hhsize 17 | regress cars hhsize 18 | 19 | 20 | // ! some other comment type -------------------------------------------------------------------------------- /samples/elm.elm: -------------------------------------------------------------------------------- 1 | --!(no space) 2 | -- ! (space) 3 | -- ! tab then tab 4 | -- ! tab then space 5 | import Html exposing (..) 6 | 7 | --! user status 8 | type UserStatus = Regular | Visitor 9 | 10 | {- 11 | ! type alias User 12 | * type alias User 13 | ? type alias User 14 | TODO typ alias Book 15 | -} 16 | type alias User = 17 | { 18 | firstName : String 19 | , lastName : String 20 | , age : Int 21 | , status : UserStatus 22 | } 23 | 24 | tom = {firstName = "tom", lastName = "john", age = 34, status = Visitor } 25 | 26 | main = 27 | text "Hello world!" 28 | 29 | {- 30 | TODO tab TODO 31 | !double tab 32 | ?triple tab 33 | *quadruple tab 34 | -} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "lib": [ 5 | "ES2020", 6 | "WebWorker" 7 | ], 8 | "rootDir": ".", 9 | "module": "NodeNext", 10 | "paths": { 11 | "@/*": [ 12 | "./src/*" 13 | ] 14 | }, 15 | "strict": true, 16 | "strictNullChecks": true, /* enable all strict type-checking options */ 17 | "alwaysStrict": true, 18 | "noImplicitAny": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "sourceMap": true, /* Report errors on unused locals. */ 22 | "skipLibCheck": true 23 | }, 24 | "exclude": [ 25 | "samples", 26 | "node_modules", 27 | ".vscode-test" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /samples/csharp.cs: -------------------------------------------------------------------------------- 1 | namespace BetterComments 2 | { 3 | /// 4 | /// The CSharpSample class. 5 | /// 6 | public class CSharpSample 7 | { 8 | public int Count = 0; 9 | 10 | public CSharpSample() 11 | { 12 | } 13 | 14 | public IncreaseCount() 15 | { 16 | // TODO implement a method that adds to the count 17 | // 18 | // ! single line comments are highlighted also 19 | 20 | /** 21 | * ! alerts can be in multilines 22 | */ 23 | 24 | /* 25 | ! you don't need a preceeding * all the time, only when block comments begin with /** 26 | */ 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /samples/tcl.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/tclsh 2 | 3 | # ! switch_cmd.tcl 4 | 5 | set domain x 6 | switch $domain { 7 | 8 | x { puts "x" } 9 | y { puts "y" } 10 | z { puts "z" } 11 | default { puts "unknown" } 12 | } 13 | 14 | proc power {base p} { 15 | set result 1 16 | while {$p > 0} { 17 | set result [expr $result * $base] 18 | set p [expr $p - 1] 19 | } 20 | return $result 21 | } 22 | 23 | set a 10 24 | set b 20 25 | 26 | if {$a == 10} { 27 | 28 | # ? if expression_1 is true then it will go to expression_2 29 | if {$b == 20} { 30 | # * if expression_2 is true then it will print the below string 31 | puts "value of a is 10 and b is 20" 32 | } 33 | } 34 | 35 | o/p: value of a is 10 and b is 20 -------------------------------------------------------------------------------- /samples/pascal.pas: -------------------------------------------------------------------------------- 1 | {$mode objfpc}{$H+}{$J-} 2 | unit AnotherUnit; 3 | interface 4 | 5 | uses Classes; 6 | 7 | { The "TComponent" type (class) is defined in the Classes unit. 8 | That's why we had to use the Classes unit above. } 9 | procedure DoSomethingWithComponent(var C: TComponent); 10 | 11 | implementation 12 | 13 | { 14 | * block comment 15 | ! block comment 16 | } 17 | 18 | // ! hello world 19 | uses SysUtils; 20 | 21 | procedure DoSomethingWithComponent(var C: TComponent); 22 | begin 23 | { The FreeAndNil procedure is defined in the SysUtils unit. 24 | Since we only refer to it's name in the implementation, 25 | it was OK to use the SysUtils unit in the "implementation" section. } 26 | FreeAndNil(C); 27 | end; 28 | 29 | end. -------------------------------------------------------------------------------- /src/utils/regex.ts: -------------------------------------------------------------------------------- 1 | const escapeCache = new Map(); 2 | /** 3 | * Escapes a given string for use in a regular expression 4 | * @param input The input string to be escaped 5 | * @returns {string} The escaped string 6 | */ 7 | export function escape(input: string): string { 8 | let escaped = escapeCache.get(input); 9 | 10 | if (!escaped) { 11 | escaped = input.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&'); // $& means the whole matched string 12 | escapeCache.set(input, escaped); 13 | } 14 | 15 | return escaped; 16 | } 17 | 18 | export const SP = '[ \\t]' as const; 19 | export const BR = '(?:\\r?\\n)' as const; 20 | export const SP_BR = '[ \\t\\r\\n]' as const; 21 | export const ANY = '[\\s\\S]' as const; 22 | export const TAG_SUFFIX = '[ \\t::]' as const; 23 | -------------------------------------------------------------------------------- /samples/shaderlab.shader: -------------------------------------------------------------------------------- 1 | // colored vertex lighting 2 | Shader "Simple colored lighting" 3 | { 4 | // a single color property 5 | Properties { 6 | _Color ("Main Color", Color) = (1,.5,.5,1) 7 | } 8 | 9 | // define one subshader 10 | SubShader 11 | { 12 | // a single pass in our subshader 13 | Pass 14 | { 15 | // use fixed function per-vertex lighting 16 | // ! this is an alert 17 | // ? this is a question 18 | // * this is a highlight 19 | // // this code has been removed 20 | // todo clean up removed code 21 | Material 22 | { 23 | Diffuse [_Color] 24 | } 25 | Lighting On 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import type * as vscode from 'vscode'; 2 | import * as log from '@/log'; 3 | import * as configuration from './configuration'; 4 | import * as definition from './definition'; 5 | import * as handler from './handler'; 6 | 7 | // this method is called when vs code is activated 8 | export async function activate(context: vscode.ExtensionContext) { 9 | try { 10 | configuration.activate(context); 11 | definition.activate(context); 12 | handler.activate(context); 13 | log.info('started successfully with configuration:', configuration.getConfigurationFlatten()); 14 | } 15 | catch (e) { 16 | log.error(e); 17 | } 18 | } 19 | 20 | export function deactivate() { 21 | configuration.deactivate(); 22 | definition.deactivate(); 23 | handler.deactivate(); 24 | } 25 | -------------------------------------------------------------------------------- /samples/coldfusion.cfc: -------------------------------------------------------------------------------- 1 | /** 2 | * * Multi-line Javadoc style comment 3 | * ! Multi-line Javadoc style comment 4 | * ? Multi-line Javadoc style comment 5 | * // Multi-line Javadoc style comment 6 | * 7 | * @COLDBOX_CONFIG_FILE The override location of the config file 8 | * @COLDBOX_APP_ROOT_PATH The location of the app on disk 9 | * @COLDBOX_APP_KEY The key used in application scope for this application 10 | * @COLDBOX_APP_MAPPING The application mapping override, only used for Flex/SOAP apps, this is auto-calculated 11 | * @COLDBOX_FAIL_FAST By default if an app is reiniting and a request hits it, we will fail fast with a message. This can be a boolean indicator or a closure. 12 | */ 13 | 14 | 15 | /* 16 | Multi 17 | Line 18 | Comments 19 | are 20 | great! 21 | */ 22 | 23 | // ! Single line comment -------------------------------------------------------------------------------- /samples/pig.pig: -------------------------------------------------------------------------------- 1 | input_lines = LOAD '/tmp/my-copy-of-all-pages-on-internet' AS (line:chararray); 2 | 3 | -- * Extract words from each line and put them into a pig bag 4 | -- ? datatype, then flatten the bag to get one word on each row 5 | words = FOREACH input_lines GENERATE FLATTEN(TOKENIZE(line)) AS word; 6 | 7 | -- TODO: filter out any words that are just white spaces 8 | filtered_words = FILTER words BY word MATCHES '\\w+'; 9 | 10 | -- ! create a group for each word 11 | word_groups = GROUP filtered_words BY word; 12 | 13 | -- count the entries in each group 14 | word_count = FOREACH word_groups GENERATE COUNT(filtered_words) AS count, group AS word; 15 | 16 | -- order the records by count 17 | ordered_word_count = ORDER word_count BY count DESC; 18 | STORE ordered_word_count INTO '/tmp/number-of-words-on-internet'; -------------------------------------------------------------------------------- /samples/verilog.v: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * Date: Aug. 15, 2006 3 | * File: Mux_2_to_1b.v (440 Examples) 4 | * ! ALERT 5 | * ? question 6 | * TODO: Some TODO 7 | * 8 | * Behavioral Model of a 2 to 1 MUX (16-bit inputs) 9 | **********************************************************************/ 10 | 11 | // ******************************************************** 12 | module mux_2to1(Y, A, B, sel); 13 | // ******************************************************** 14 | // ! alert 15 | // ? question 16 | // TODO: Some TODO 17 | 18 | //// Commented out code 19 | 20 | output [15:0] Y; 21 | input [15:0] A, B; 22 | input sel; 23 | reg [15:0] Y; 24 | always @(A or B or sel) 25 | if (sel == 1'b0) 26 | Y = A; 27 | else 28 | Y = B; 29 | endmodule -------------------------------------------------------------------------------- /samples/typescriptreact.tsx: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | 5 | import Hello from './containers/Hello'; 6 | import { Provider } from 'react-redux'; 7 | import { createStore } from 'redux'; 8 | import { enthusiasm } from './reducers/index'; 9 | import { StoreState } from './types/index'; 10 | 11 | import './index.css'; 12 | 13 | /** 14 | * some comments here 15 | * ! to alert 16 | * * to highlight 17 | * ? to question 18 | */ 19 | 20 | /* 21 | ! and the other format 22 | */ 23 | 24 | // finally 25 | // ! single line comments 26 | const store = createStore(enthusiasm, { 27 | enthusiasmLevel: 1, 28 | languageName: 'TypeScript', 29 | }); 30 | 31 | ReactDOM.render( 32 | 33 | {/* ! this is the react comment alert */} 34 | 35 | , 36 | document.getElementById('root') as HTMLElement 37 | ); 38 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/dist/**/*.js" 14 | ], 15 | "preLaunchTask": "npm: watch" 16 | }, 17 | { 18 | "name": "Web Extension ", 19 | "type": "extensionHost", 20 | "debugWebWorkerHost": true, 21 | "request": "launch", 22 | "args": [ 23 | "--extensionDevelopmentPath=${workspaceFolder}", 24 | "--extensionDevelopmentKind=web" 25 | ], 26 | "outFiles": [ 27 | "${workspaceFolder}/dist/**/*.js" 28 | ], 29 | "preLaunchTask": "npm: watch-web" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Sleep micro second 3 | * @param ms micro second to sleep 4 | */ 5 | export function sleep(ms: number) { 6 | return new Promise(resolve => setTimeout(resolve, ms)); 7 | } 8 | 9 | export function debounce any>(fn: T, delay: number) { 10 | let timer: NodeJS.Timeout | undefined; 11 | return function (this: any, ...args: Parameters) { 12 | if (timer) { 13 | clearTimeout(timer); 14 | } 15 | timer = setTimeout(() => fn.apply(this, args), delay); 16 | } as T; 17 | } 18 | 19 | export function generateUUID() { 20 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { 21 | const r = Math.random() * 16 | 0; 22 | const v = c === 'x' ? r : (r & 0x3 | 0x8); 23 | return v.toString(16); 24 | }); 25 | } 26 | 27 | export class CancelError extends Error { 28 | constructor(message: string = 'Operation canceled') { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/dart.dart: -------------------------------------------------------------------------------- 1 | class Spacecraft { 2 | String name; 3 | DateTime launchDate; 4 | 5 | // ! Constructor, with syntactic sugar for assignment to members. 6 | Spacecraft(this.name, this.launchDate) { 7 | // Initialization code goes here. 8 | } 9 | 10 | // * Named constructor that forwards to the default one. 11 | Spacecraft.unlaunched(String name) : this(name, null); 12 | 13 | int get launchYear => 14 | launchDate?.year; // read-only non-final property 15 | 16 | /* 17 | Block comment 18 | ! alert 19 | * info 20 | ? question 21 | TODO: do some stuff 22 | */ 23 | 24 | // ? Method. 25 | void describe() { 26 | print('Spacecraft: $name'); 27 | if (launchDate != null) { 28 | int years = new DateTime.now() 29 | .difference(launchDate) 30 | .inDays ~/ 31 | 365; 32 | print('Launched: $launchYear ($years years ago)'); 33 | } else { 34 | print('Unlaunched'); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config'; 2 | 3 | export default antfu( 4 | { 5 | ignores: ['samples'], 6 | }, 7 | { 8 | // style 9 | rules: { 10 | 'style/quote-props': ['warn', 'as-needed'], 11 | 'style/semi': ['warn', 'always'], 12 | 'style/max-statements-per-line': ['warn', { max: 1 }], 13 | curly: ['warn', 'all'], 14 | 'style/member-delimiter-style': [ 15 | 'warn', 16 | { 17 | multiline: { delimiter: 'semi', requireLast: true }, 18 | singleline: { delimiter: 'semi', requireLast: false }, 19 | multilineDetection: 'brackets', 20 | }, 21 | ], 22 | 'unused-imports/no-unused-vars': ['error', { vars: 'all', args: 'none' }], 23 | 'no-cond-assign': 'off', 24 | }, 25 | }, 26 | { 27 | files: ['package.json'], 28 | rules: { 29 | 'jsonc/indent': ['error', 4], 30 | }, 31 | }, 32 | { 33 | rules: { 34 | 'unicorn/prefer-node-protocol': 'off', 35 | }, 36 | }, 37 | ); 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2017 Edwin Xu 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /src/definition/event.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as configuration from '../configuration'; 3 | import * as definition from './definition'; 4 | 5 | export type OnDidChangeCallback = () => void; 6 | 7 | const onDidChangeCallbacks: OnDidChangeCallback[] = []; 8 | export function onDidChange(callback: OnDidChangeCallback) { 9 | onDidChangeCallbacks.push(callback); 10 | } 11 | 12 | let disposable: vscode.Disposable | undefined; 13 | export function activate(context: vscode.ExtensionContext) { 14 | const refresh = () => { 15 | definition.refresh(); 16 | 17 | // Run change callbacks 18 | for (const callback of onDidChangeCallbacks) { 19 | callback(); 20 | } 21 | }; 22 | 23 | // Refresh languages definitions after extensions changed 24 | disposable = vscode.extensions.onDidChange(refresh, null, context.subscriptions); 25 | 26 | configuration.onDidChange(refresh); 27 | 28 | // refresh once 29 | refresh(); 30 | } 31 | 32 | export function deactivate() { 33 | if (disposable) { 34 | disposable.dispose(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/promise.ts: -------------------------------------------------------------------------------- 1 | import { CancelError } from './utils'; 2 | 3 | export type Resolve = (value?: any) => void; 4 | 5 | export interface PromiseCancelable extends Promise { 6 | cancel: () => PromiseCancelable; 7 | isCancel: () => boolean; 8 | } 9 | 10 | /** 11 | * 将一个promise转换为一个可取消的promise 12 | * @param {Promise} task 希望被转换的promise实例 13 | * @returns {Promise} 返回具有cancel()&isCancel()的promise对象 14 | */ 15 | export function Cancelable(task: Promise) { 16 | let _reject: Resolve; 17 | let isCancel = false; 18 | const cancelP = new Promise((resolve, reject) => { 19 | _reject = reject; 20 | }); 21 | const p = Promise.race([task, cancelP]) as PromiseCancelable; 22 | /*** 23 | * 调用cancel时可能promise状态已经变为成功, 24 | * 所以不能在cancel里面改变isCancel 25 | * 只有catch的原因是cancel才代表被取消成功了 26 | */ 27 | p.catch((reason) => { 28 | if (reason instanceof CancelError) { 29 | isCancel = true; 30 | } 31 | }); 32 | 33 | p.cancel = () => { 34 | _reject(new CancelError()); 35 | return p; 36 | }; 37 | p.isCancel = () => { 38 | return isCancel; 39 | }; 40 | return p; 41 | } 42 | -------------------------------------------------------------------------------- /src/configuration/event.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigurationFlatten } from './configuration'; 2 | import * as vscode from 'vscode'; 3 | import { getConfigurationFlatten, refresh } from './configuration'; 4 | 5 | export type OnDidChangeCallback = (config: ConfigurationFlatten) => void; 6 | 7 | const onDidChangeCallbacks: OnDidChangeCallback[] = []; 8 | export function onDidChange(callback: OnDidChangeCallback) { 9 | onDidChangeCallbacks.push(callback); 10 | } 11 | 12 | let disposable: vscode.Disposable | undefined; 13 | 14 | export function activate(context: vscode.ExtensionContext) { 15 | // Refresh configuration after configuration changed 16 | disposable = vscode.workspace.onDidChangeConfiguration( 17 | (event) => { 18 | if (!event.affectsConfiguration('better-comments')) { 19 | return; 20 | } 21 | 22 | refresh(); 23 | 24 | const config = getConfigurationFlatten(); 25 | 26 | // Run change callback 27 | for (const callback of onDidChangeCallbacks) { 28 | callback(config); 29 | } 30 | }, 31 | null, 32 | context.subscriptions, 33 | ); 34 | } 35 | 36 | export function deactivate() { 37 | if (disposable) { 38 | disposable.dispose(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/handler/handler.ts: -------------------------------------------------------------------------------- 1 | import type { Handler, UpdateParams } from './modules/common'; 2 | import * as configuration from '../configuration'; 3 | import { CommonHandler } from './modules/common'; 4 | import { PlainTextHandler } from './modules/plaintext'; 5 | import { ReactHandler } from './modules/react'; 6 | import { ShellscriptHandler } from './modules/shellscript'; 7 | 8 | const cached = new Map(); 9 | 10 | function newHandler(languageId: string): Handler { 11 | switch (languageId) { 12 | case 'javascriptreact': 13 | case 'typescriptreact': 14 | return new ReactHandler(languageId); 15 | case 'shellscript': 16 | return new ShellscriptHandler(languageId); 17 | case 'plaintext': 18 | return new PlainTextHandler(languageId); 19 | default: 20 | return new CommonHandler(languageId); 21 | } 22 | } 23 | 24 | function useHandler(languageId: string): Handler { 25 | let handler = cached.get(languageId); 26 | 27 | if (!handler) { 28 | handler = newHandler(languageId); 29 | cached.set(languageId, handler); 30 | } 31 | 32 | return handler; 33 | } 34 | 35 | export function triggerUpdateDecorations(params: UpdateParams) { 36 | const configuratgion = configuration.getConfigurationFlatten(); 37 | return useHandler(params.editor.document.languageId).triggerUpdateDecorations({ ...params, timeout: configuratgion.updateDelay }); 38 | } 39 | -------------------------------------------------------------------------------- /src/utils/str.ts: -------------------------------------------------------------------------------- 1 | import { escape } from './regex'; 2 | 3 | export function trim(str: string, char?: string) { 4 | const excaped = char !== undefined ? escape(char) : '\\s'; 5 | return str.replace(new RegExp(`^${excaped}+|${excaped}+$`, 'g'), ''); 6 | } 7 | 8 | export function trimLeft(str: string, char?: string) { 9 | const excaped = char !== undefined ? escape(char) : '\\s'; 10 | return str.replace(new RegExp(`^${excaped}+`, 'g'), ''); 11 | } 12 | 13 | export function trimRight(str: string, char?: string) { 14 | const excaped = char !== undefined ? escape(char) : '\\s'; 15 | return str.replace(new RegExp(`${excaped}+$`, 'g'), ''); 16 | } 17 | 18 | export function replaceWithSpace(input: string, startIndex: number, endIndex: number): string { 19 | if (startIndex < 0 || endIndex >= input.length || startIndex > endIndex) { 20 | throw new RangeError('Invalid range specified'); 21 | } 22 | 23 | let result = input.slice(0, startIndex); 24 | 25 | for (let i = startIndex; i <= endIndex; i++) { 26 | const char = input[i]; 27 | if (char !== ' ' && char !== '\t' && char !== '\n') { 28 | result += ' '; 29 | } 30 | else { 31 | result += char; 32 | } 33 | } 34 | 35 | result += input.slice(endIndex + 1); 36 | 37 | return result; 38 | } 39 | 40 | export function isString(value: any): value is string { 41 | return typeof value === 'string' || value instanceof String; 42 | } 43 | -------------------------------------------------------------------------------- /samples/python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | my_method does a thing. There are many like it, but this one is mine. 5 | ? Do we really need this? 6 | ! Deprecated 7 | @param my_param Do some stuff with this 8 | """ 9 | 10 | """""""""""" 11 | if ( 12 | foo( 13 | x 14 | ) 15 | != bar # this line should not be highlighted 16 | ): 17 | return 18 | 19 | """""""""""" 20 | 21 | # ! Import the modules 22 | import sys 23 | import random 24 | 25 | # * var to set up loop 26 | myVar = True 27 | 28 | # ? will this loop ever terminate? 29 | while myVar: 30 | 31 | # TODO: localise the output 32 | question = raw_input("Ask the magic 8 ball a question: (press enter to quit) ") 33 | 34 | answers = random.randint(1,8) 35 | 36 | if question == "": 37 | sys.exit() 38 | 39 | elif answers == 1: 40 | print "It is certain" 41 | 42 | elif answers == 2: 43 | print "Outlook good" 44 | 45 | elif answers == 3: 46 | print "You may rely on it" 47 | 48 | elif answers == 4: 49 | print "Ask again later" 50 | 51 | elif answers == 5: 52 | print "Concentrate and ask again" 53 | 54 | elif answers == 6: 55 | print "Reply hazy, try again" 56 | 57 | elif answers == 7: 58 | print "My reply is no" 59 | 60 | elif answers == 8: 61 | print "My sources say no" 62 | -------------------------------------------------------------------------------- /samples/vuejs.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 63 | -------------------------------------------------------------------------------- /samples/typescript.ts: -------------------------------------------------------------------------------- 1 | export class TestClass { 2 | /** 3 | * Test Method 4 | * * Important information is highlighted 5 | * ! Deprecated method, do not use 6 | * ? Should this method be exposed through API? 7 | * TODO: refactor this method to conform to API 8 | * Ensure continuous lines with indentation. 9 | * This is a multi-line TODO comment that 10 | * should be highlighted properly. 11 | * 12 | * @param param === condition 13 | * ? true This line should not be highlighted 14 | * : false 15 | */ 16 | public TestMethod(param: any): void { 17 | /* # this is inline block comment */ 18 | const testVar = 123; 19 | 20 | //* This should not be highlighted 21 | if (testVar > 0) { 22 | throw new TypeError('Some error'); // ! this is an alert 23 | } 24 | 25 | // ? This is a query 26 | const x = 1; 27 | 28 | // // this.lineOfCode() == commentedOut; 29 | 30 | // TODO: write some test cases 31 | 32 | /* 33 | todo: multi-line in block comment example 34 | Ensure continuous lines with indentation. 35 | This is a multi-line TODO comment 36 | that should be highlighted properly. 37 | */ 38 | 39 | 40 | // ! line comment multi-line mode 41 | // 42 | // TODO: multi-line in block comment example 43 | // Ensure continuous lines with indentation. 44 | // This is a multi-line TODO comment 45 | // that should be highlighted properly. 46 | // 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/handler/event.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as handler from './handler'; 3 | 4 | export type OnDidChangeCallback = (event: vscode.TextDocumentChangeEvent, editor?: vscode.TextEditor) => void; 5 | 6 | const onDidChangeCallbacks: OnDidChangeCallback[] = []; 7 | export function onDidChange(callback: OnDidChangeCallback) { 8 | onDidChangeCallbacks.push(callback); 9 | } 10 | 11 | export function activate(context: vscode.ExtensionContext) { 12 | // Loop all visible editor for the first time and initialise the regex 13 | for (const editor of vscode.window.visibleTextEditors) { 14 | handler.triggerUpdateDecorations({ editor }); 15 | } 16 | 17 | // * Handle active file changed 18 | vscode.window.onDidChangeActiveTextEditor( 19 | async (editor) => { 20 | if (editor) { 21 | // Update decorations for newly active file 22 | handler.triggerUpdateDecorations({ editor }); 23 | } 24 | }, 25 | null, 26 | context.subscriptions, 27 | ); 28 | 29 | // * Handle file contents changed 30 | vscode.workspace.onDidChangeTextDocument( 31 | (event) => { 32 | // Trigger updates if the text was changed in the visible editor 33 | const editor = vscode.window.visibleTextEditors.find(e => e.document === event.document); 34 | if (editor) { 35 | handler.triggerUpdateDecorations({ editor }); 36 | } 37 | 38 | // Run change callbacks 39 | for (const callback of onDidChangeCallbacks) { 40 | callback(event, editor); 41 | } 42 | }, 43 | null, 44 | context.subscriptions, 45 | ); 46 | } 47 | 48 | export function deactivate() { 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/handler/modules/shellscript.ts: -------------------------------------------------------------------------------- 1 | import type { LineCommentSlice, PickParams } from './common'; 2 | import * as definition from '@/definition'; 3 | import { BR, escape, SP } from '@/utils/regex'; 4 | import { CommonHandler } from './common'; 5 | 6 | export class ShellscriptHandler extends CommonHandler { 7 | protected async pickLineCommentSlices(params: PickParams): Promise> { 8 | this.verifyTaskID(params.taskID); 9 | 10 | const { lineComments } = await definition.getAvailableComments(params.editor.document.languageId); 11 | if (!lineComments || !lineComments.length) { 12 | return []; 13 | } 14 | 15 | const slices: LineCommentSlice[] = []; 16 | 17 | const marks = lineComments.map(s => `${escape(s)}+`).join('|'); 18 | 19 | const exp = new RegExp(`(?
    .?)(?${marks}).*?(?:${BR}${SP}*\\1.*?)*(?:${BR}|$)`, 'g');
    20 |     let block: RegExpExecArray | null;
    21 |     while ((block = exp.exec(params.text))) {
    22 |       this.verifyTaskID(params.taskID);
    23 | 
    24 |       const start = params.offset + block.index;
    25 |       const end = start + block[0].length;
    26 | 
    27 |       if (params.processed.find(([pStart, pEnd]) => pStart <= start && end <= pEnd)) {
    28 |         // skip if already processed
    29 |         continue;
    30 |       }
    31 |       // store processed range
    32 |       params.processed.push([start, end]);
    33 | 
    34 |       if (block.groups!.PRE === '$') {
    35 |         continue; // skip if line starts with $
    36 |       }
    37 | 
    38 |       slices.push({
    39 |         start,
    40 |         end,
    41 |         comment: block[0],
    42 |         mark: block.groups!.MARK,
    43 |       });
    44 |     }
    45 | 
    46 |     return slices;
    47 |   }
    48 | }
    49 | 
    
    
    --------------------------------------------------------------------------------
    /src/handler/modules/react.ts:
    --------------------------------------------------------------------------------
     1 | import type { BlockCommentSlice, PickParams } from './common';
     2 | import * as definition from '@/definition';
     3 | import { ANY, BR, escape } from '@/utils/regex';
     4 | import { CommonHandler } from './common';
     5 | 
     6 | export class ReactHandler extends CommonHandler {
     7 |   protected async pickBlockCommentSlices(params: PickParams): Promise> {
     8 |     this.verifyTaskID(params.taskID);
     9 | 
    10 |     const { blockComments } = await definition.getAvailableComments(params.editor.document.languageId);
    11 |     if (!blockComments || !blockComments.length) {
    12 |       return [];
    13 |     }
    14 | 
    15 |     const slices: BlockCommentSlice[] = [];
    16 | 
    17 |     for (const marks of blockComments) {
    18 |       this.verifyTaskID(params.taskID);
    19 | 
    20 |       const markStart = escape(marks[0]);
    21 |       const markEnd = escape(marks[1]);
    22 |       const exp = new RegExp(`(?
    (?:^|${BR})\\s*|\{\\s*)(?${markStart})(?${ANY}*?)(?${markEnd})`, 'g');
    23 | 
    24 |       let block: RegExpExecArray | null;
    25 |       while ((block = exp.exec(params.text))) {
    26 |         this.verifyTaskID(params.taskID);
    27 | 
    28 |         const start = params.offset + block.index + block.groups!.PRE.length;
    29 |         const end = params.offset + block.index + block[0].length;
    30 | 
    31 |         if (params.processed.find(([pStart, pEnd]) => pStart <= start && end <= pEnd)) {
    32 |           // skip if already processed
    33 |           continue;
    34 |         }
    35 |         // store processed range
    36 |         params.processed.push([start, end]);
    37 | 
    38 |         slices.push({
    39 |           start,
    40 |           end,
    41 |           comment: block[0],
    42 |           content: block.groups!.CONTENT,
    43 |           marks,
    44 |         });
    45 |       }
    46 |     }
    47 | 
    48 |     return slices;
    49 |   }
    50 | }
    51 | 
    
    
    --------------------------------------------------------------------------------
    /samples/cobol.cbl:
    --------------------------------------------------------------------------------
     1 |       $ SET SOURCEFORMAT"FREE"
     2 | IDENTIFICATION DIVISION.
     3 | PROGRAM-ID.  PerformFormat3.
     4 | AUTHOR.  Michael Coughlan.
     5 | * Demonstrates the use of the PERFORM..UNTIL.
     6 | * The PERFORM..UNTIL is most often used to process a 
     7 | * stream of data where the length of the stream can not 
     8 | * be determined in advance.
     9 | * Pay particular attention to the way the number stream is 
    10 | * processed in this program.
    11 | * Note how the ON SIZE ERROR can be used to detect when the
    12 | * result of a computation is tot big for the data-item intended
    13 | * to hold it.
    14 | * The INITIALIZE verb sets a data-item to its initial or 
    15 | * starting value.
    16 | *> TODO: Check if this format is correct
    17 | DATA DIVISION.
    18 | WORKING-STORAGE SECTION.
    19 | 01 IterCount           PIC 99  VALUE ZEROS.
    20 |    88 MaxCountReached  VALUE 99.
    21 | 01 UserInput           PIC 99  VALUE ZEROS.
    22 |    88 EndOfUserInput   VALUE ZEROS.
    23 | 01 RunningTotal        PIC 999 VALUE ZEROS.
    24 | 01 AverageValue        PIC 99  VALUES ZEROS.
    25 | 
    26 | PROCEDURE DIVISION.
    27 | Begin.
    28 |     PERFORM UNTIL IterCount = 5
    29 |        DISPLAY "IterCount = " IterCount
    30 |        ADD 1 TO IterCount
    31 |     END-PERFORM
    32 |     DISPLAY "Finished in line Perform." *> ! comment here
    33 | 
    34 |     INITIALIZE Itercount
    35 | 
    36 |     DISPLAY "Enter a stream of up to 99 numbers."
    37 |     DISPLAY "Each number must be in the range 1-99.  Enter 0 to stop."
    38 |     DISPLAY "Enter number :- " WITH NO ADVANCING
    39 |     ACCEPT UserInput
    40 |     PERFORM GetUserInput UNTIL EndOfUserInput OR MaxCountReached
    41 | 
    42 |     DISPLAY "The final total is - " RunningTotal
    43 |     DISPLAY "The final count is - " IterCount
    44 |     COMPUTE AverageValue = RunningTotal / IterCount
    45 |     DISPLAY "The average value entered is - " AverageValue
    46 |     STOP RUN.
    47 | 
    48 | 
    49 | GetUserInput.
    50 |     ADD UserInput TO RunningTotal
    51 |         ON SIZE ERROR DISPLAY "Error - new total too large for data-item."
    52 |         NOT ON SIZE ERROR ADD 1 TO IterCount END-ADD
    53 |     END-ADD
    54 |     DISPLAY "Total so far is - " RunningTotal
    55 |     DISPLAY "Count so far is - " IterCount
    56 |     DISPLAY "Enter number :- " WITH NO ADVANCING
    57 |     ACCEPT UserInput.
    58 | 
    59 | 
    
    
    --------------------------------------------------------------------------------
    /samples/django.html:
    --------------------------------------------------------------------------------
     1 | {% load staticfiles %}
     2 | 
     3 | 
     4 | 
     5 |     
     6 |     
     7 |     
     8 |     
     9 |     
    10 | 
    11 |     {% block title %}Example{% endblock %}
    12 | 
    13 |     
    14 |     
    15 | 
    16 |     
    17 |     
    18 |     {% block css %}{% endblock %}
    19 | 
    20 | 
    21 | 
    22 | {# some text #}
    23 | 
    24 | 
    25 | 
    26 | {% block navbar %}
    27 |     {% include  '_items/navbar.html' %}
    28 | {% endblock %}
    29 | 
    30 | 
    31 | {% for message in messages %} 32 |
    33 | 34 | {{ message }} 35 |
    36 | {% endfor %} 37 |
    38 | 39 | 40 | 41 |
    42 | {% block content %} 43 | {% endblock %} 44 | 45 |
    46 | 47 |
    48 |

    © Company 2017

    49 |
    50 |
    51 | 52 | 53 | 54 | 56 | 57 | 60 | 63 | 64 | 65 | {% block js %}{% endblock %} 66 | 67 | -------------------------------------------------------------------------------- /samples/modernpascal.pp: -------------------------------------------------------------------------------- 1 | program class_Example; 2 | 3 | // ! hello 4 | // * hello 5 | Uses 6 | strings; 7 | 8 | Type 9 | HookRec=Record 10 | Name:String; 11 | Value:TStringArray; 12 | End; 13 | TSlim=Class 14 | Version:String; 15 | Container:String; 16 | Apps:TStringArray; 17 | Name:String; 18 | Middleware:String; 19 | Error:String; 20 | notFound:String; 21 | hooks:Array [0..5] of HookRec; 22 | Mem:Pointer; 23 | MemSize:Longint; 24 | End; 25 | TNotSoSlim=Class(TSlim) 26 | AnotherVar:String; 27 | End; 28 | TSomeOther=Class 29 | Happy:Boolean; 30 | Name:String; 31 | End; 32 | 33 | procedure TSlim.Free; 34 | begin 35 | with Self do begin 36 | FreeMem(Mem, MemSize); 37 | Writeln('Free: ',MemSize); 38 | End; 39 | end; 40 | 41 | procedure TSlim.Init; // constructor does not need to be published. 42 | begin 43 | with Self do begin 44 | MemSize:=2048; 45 | GetMem(Mem,MemSize); 46 | Container:='none'; 47 | Version:='2.6.1p'; 48 | hooks:=[ ['slim.before',[]], 49 | ['slim.before.router',[]], 50 | ['slim.before.dispatch',[]], 51 | ['slim.after.dispatch',[]], 52 | ['slim.after.router',[]], 53 | ['slim.after',[]] ]; 54 | TMethod(@Free):=[@TSlim.Free, @Self]; 55 | end; 56 | end; 57 | 58 | procedure TNotSoSlim.Free; override; 59 | Begin 60 | inherited; 61 | Writeln('AnotherVar layer: ',AnotherVar); 62 | end; 63 | 64 | procedure TNotSoSlim.Init; override; 65 | begin 66 | inherited; 67 | TMethod(@Free):=[@TNotSoSlim.Free, @Self]; 68 | end; 69 | 70 | procedure TSomeOther.Free; 71 | begin 72 | // nada 73 | end; 74 | 75 | procedure TSomeOther.Init; 76 | begin 77 | Self.Happy:=True; 78 | TMethod(@Free):=[@TSomeOther.Free, @Self]; 79 | end; 80 | 81 | var 82 | inst:TSlim; 83 | inst2:TSlim; 84 | inst3:TNotSoSlim; 85 | inst4:TSomeOther; 86 | 87 | begin 88 | Writeln('Testing...',1,2,3,' ',4); 89 | inst.Init; 90 | inst2.Init; 91 | inst3.Init; 92 | inst4.Init; 93 | inst3.AnotherVar:="Cool!"; 94 | inst2.Version:='3.0'; 95 | Writeln('v',inst.Version); 96 | Writeln('v',inst2.Version); 97 | Writeln('v',inst3.Version); 98 | Writeln('cont:',inst.Container); 99 | Writeln('a:',inst3.AnotherVar); 100 | Writeln('h:',inst4.Happy); 101 | inst.Free; 102 | inst2.Free; 103 | inst3.Free; 104 | inst4.Free; 105 | end. -------------------------------------------------------------------------------- /samples/AL.al: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT License. See License.txt in the project root for license information. 4 | // ------------------------------------------------------------------------------------------------ 5 | 6 | // ! Codeunit for creating random greetings 7 | codeunit 50110 GreetingsManagement 8 | { 9 | // ? Get a translated 'Hello World' string. 10 | // * Thanks to https://www.bing.com/translator/ 11 | local procedure GetHelloWorldText(GreetingNo : Integer) : Text; 12 | begin 13 | case GreetingNo of 14 | 1: exit('Arabic: مرحبا بالعالم'); 15 | 2: exit('Bulgarian: Здравей, свят'); 16 | 3: exit('Cantonese: 世界你好'); 17 | 4: exit('Greek: Γεια σου κόσμε'); 18 | 5: exit('Korean: 전 세계 여러분 안녕하세요'); 19 | 6: exit('Thai: หวัดดีชาวโลก'); 20 | 7: exit('Hindi: हैलो वर्ल्ड'); 21 | 8: exit('Japanese: ハローワールド'); 22 | 9: exit('Danish: Hej verden'); 23 | 10: exit('Polish: Witaj świecie'); 24 | 11: exit('Pig Latin: Ellohay Orldway'); 25 | 12: exit('Hungarian: Szia, világ!'); 26 | 13: exit('Flemish: Hej wereld'); 27 | 14: exit('Dutch: Hallo wereld'); 28 | 15: exit('French: Bonjour le monde'); 29 | 16: exit('Finnish: Hei maailma'); 30 | 17: exit('Russian: Привет, мир!'); 31 | 18: exit('Czech: Ahoj světe'); 32 | 19: exit('German: Hallo Welt'); 33 | 20: exit('Lithuanian: Labas, pasauli!'); 34 | 21: exit('Afrikaans: Hallo wêreld'); 35 | 22: exit('Bakke Snavvendt: Wello Horld'); 36 | 23: exit('1337 : h3ll0 w0rld!'); 37 | 24: exit('|_337: |-|3|_|_0 \\/\\/0|2|_|)!'); 38 | 25: exit('Morse code: ...././.-../.-../---//.--/---/.-./.-../-../-.-.--////'); 39 | 26: exit('Ballon script: Ⓗⓔⓛⓛⓞ Ⓦⓞⓡⓛⓓ!'); 40 | 27: exit('Braille: ⠠⠓⠑⠇⠇⠕ ⠠⠺⠕⠗⠇⠙⠖'); 41 | 28: exit('Español: Hola Mundo!'); 42 | 29: exit('Albanian: Përshëndetje, Botë!'); 43 | 30: exit('Turkish: Merhaba Dünya!'); 44 | 31: exit('Tamil: வணக்கம்'); 45 | 32: exit('Sinhalese: ආයුබෝවන්'); 46 | 33: exit('Swahili: Salamu, Dunia'); 47 | 34: exit('Catalan: Hola món'); 48 | 35: exit('Icelandic: Halló heimur'); 49 | 36: exit('Gaeilge: Dia duit an domhan'); 50 | else 51 | exit('Hello, World'); // Default to the good old one. 52 | end; 53 | end; 54 | 55 | // Gets a random greeting. 56 | procedure GetRandomGreeting() : Text; 57 | begin 58 | Randomize; 59 | exit(GetHelloWorldText(Random(37))); 60 | end; 61 | 62 | /* 63 | ! hello world 64 | */ 65 | 66 | /** 67 | * ! hello world 68 | */ 69 | } -------------------------------------------------------------------------------- /samples/racket.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ;;; ! Functions for 2d drawing and transformation 4 | 5 | (require lang/posn) 6 | 7 | (struct pos (x y) #:transparent) 8 | 9 | (define (move-pos a-pos a-direction a-speed) 10 | (define r (degrees->radians a-direction)) 11 | (pos (+ (pos-x a-pos) (* a-speed (cos r))) 12 | (+ (pos-y a-pos) (* a-speed (sin r))))) 13 | 14 | (define (add-direction-speeds d1 s1 d2 s2) 15 | ; Given two direction & speed pairs, calculate the 16 | ; combined effect and return new direction and speed 17 | (if (and (zero? s1) (zero? s2)) 18 | (list d1 0) 19 | (let* ([vec1 (move-pos (pos 0 0) d1 s1)] 20 | [vec2 (move-pos (pos 0 0) d2 s2)] 21 | [c-vec (pos (+ (pos-x vec1) (pos-x vec2)) 22 | (+ (pos-y vec1) (pos-y vec2)))] 23 | [direction (radians->degrees 24 | (atan (pos-y c-vec) 25 | (pos-x c-vec)))] 26 | [speed (sqrt (+ (sqr (pos-x c-vec)) 27 | (sqr (pos-y c-vec))))]) 28 | (list direction speed)))) 29 | 30 | (define (pos->posn points) 31 | (map (λ (p) (make-posn (pos-x p) (pos-y p))) 32 | points)) 33 | 34 | ;; TODO ----------------------------------------------------------- 35 | 36 | (define (inside-circle? circle-pos radius a-pos) 37 | (define distance 38 | (sqrt (+ (expt (- (pos-x a-pos) (pos-x circle-pos)) 2) 39 | (expt (- (pos-y a-pos) (pos-y circle-pos)) 2)))) 40 | (<= distance radius)) 41 | 42 | (define (between? a x y) 43 | "Is a between x and y?" 44 | (or (<= x a y) 45 | (>= x a y))) 46 | 47 | (define (inside-rect? rpos1 rpos2 a-pos) 48 | "Is a-pos inside the rectangle defined by corners rpos1 and 2?" 49 | (and (between? (pos-x a-pos) (pos-x rpos1) (pos-x rpos2)) 50 | (between? (pos-y a-pos) (pos-y rpos1) (pos-y rpos2)))) 51 | 52 | (define (direction-from-a-to-b pos1 pos2) 53 | "What's the direction/bearing from pos1 to pos2?" 54 | (let ([vector (pos (- (pos-x pos2) (pos-x pos1)) 55 | (- (pos-y pos2) (pos-y pos1)))]) 56 | (radians->degrees 57 | (atan (pos-y vector) (pos-x vector))))) 58 | 59 | (define (inside-triangle? points a-pos) 60 | "Is a-pos inside this triangle defined by the 3 points?" 61 | (let* ([angle1-2 (direction-from-a-to-b (first points) (second points))] 62 | [angle1-3 (direction-from-a-to-b (first points) (third points))] 63 | [angle1-a (direction-from-a-to-b (first points) a-pos)] 64 | [angle2-1 (direction-from-a-to-b (second points) (first points))] 65 | [angle2-3 (direction-from-a-to-b (second points) (third points))] 66 | [angle2-a (direction-from-a-to-b (second points) a-pos)]) 67 | (and (between? angle1-a angle1-2 angle1-3) 68 | (between? angle2-a angle2-1 angle2-3)))) 69 | 70 | ;; ----------------------------------------------------------- 71 | 72 | (provide pos pos-x pos-y pos->posn 73 | move-pos add-direction-speeds 74 | between? inside-circle? inside-rect? inside-triangle? 75 | direction-from-a-to-b) -------------------------------------------------------------------------------- /src/definition/definition.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as extConfig from '../configuration'; 3 | import * as langs from './modules'; 4 | 5 | const cached = new Map(); 6 | 7 | export function useLanguage(langId: string): langs.Language { 8 | let lang = cached.get(langId); 9 | 10 | if (!lang) { 11 | lang = langs.useLanguage(langId); 12 | cached.set(langId, lang); 13 | } 14 | 15 | return lang; 16 | } 17 | 18 | /** 19 | * Refresh the language cache 20 | */ 21 | export function refresh() { 22 | cached.clear(); 23 | 24 | for (const extension of vscode.extensions.all) { 25 | const packageJSON = extension.packageJSON; 26 | for (const language of packageJSON?.contributes?.languages || []) { 27 | // if language is not defined, skip it 28 | if (!language || !language.id) { 29 | continue; 30 | } 31 | 32 | const lang = useLanguage(language.id); 33 | 34 | const configUri = language.configuration 35 | ? vscode.Uri.joinPath(extension.extensionUri, language.configuration) 36 | : undefined; 37 | lang.setConfigurationUri(configUri); 38 | 39 | const embeddedLanguages = lang.getEmbeddedLanguages(); 40 | if (embeddedLanguages.size > 0) { 41 | // If already set embedded languages, skip it 42 | continue; 43 | } 44 | for (const grammar of packageJSON.contributes?.grammars || []) { 45 | if (grammar.language !== language.id || !grammar.embeddedLanguages) { 46 | continue; 47 | } 48 | for (const embeddedLanguageCode of Object.values(grammar.embeddedLanguages)) { 49 | embeddedLanguages.add(embeddedLanguageCode as string); 50 | } 51 | } 52 | 53 | lang.setEmbeddedLanguages(embeddedLanguages); 54 | } 55 | } 56 | 57 | const extConf = extConfig.getConfigurationFlatten(); 58 | for (const language of extConf.languages) { 59 | const lang = useLanguage(language.id); 60 | 61 | if (language?.comments?.lineComment || language?.comments?.blockComment?.length) { 62 | lang.setComments(language.comments); 63 | } 64 | 65 | if (language.embeddedLanguages) { 66 | for (const embeddedLanguageCode of language.embeddedLanguages) { 67 | lang.addEmbeddedLanguage(embeddedLanguageCode); 68 | } 69 | } 70 | 71 | lang.setUseDocComment(language.useDocComment); 72 | } 73 | } 74 | 75 | /** 76 | * Gets the configuration information for the specified language 77 | */ 78 | export async function getAvailableComments(langId: string): Promise { 79 | const language = useLanguage(langId); 80 | 81 | let availableComments = language.getAvailableComments(); 82 | 83 | if (availableComments) { 84 | return availableComments; 85 | } 86 | 87 | const lineComments = new Set(); 88 | const blockComments = new Map(); 89 | async function addCommentByLang(lang?: langs.Language) { 90 | if (!lang) { 91 | return; 92 | } 93 | 94 | const comments = await lang.getComments(); 95 | 96 | if (comments?.lineComment) { 97 | lineComments.add(comments.lineComment); 98 | } 99 | 100 | if (comments?.blockComment) { 101 | const key = `${comments.blockComment[0]}${comments.blockComment[1]}`; 102 | blockComments.set(key, comments.blockComment); 103 | } 104 | } 105 | 106 | await addCommentByLang(language); 107 | 108 | const embeddedLanguages = language.getEmbeddedLanguages(); 109 | for (const embeddedLanguageCode of embeddedLanguages) { 110 | const lang = useLanguage(embeddedLanguageCode); 111 | await addCommentByLang(lang); 112 | } 113 | 114 | availableComments = { 115 | lineComments: Array.from(lineComments), 116 | blockComments: [...blockComments.values()], 117 | }; 118 | 119 | language.setAvailableComments(availableComments); 120 | 121 | return availableComments; 122 | } 123 | -------------------------------------------------------------------------------- /samples/nim.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # The Nim Compiler 4 | # (c) Copyright 2015 Andreas Rumpf 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | # * adding some sample comments for better-comments 11 | when defined(gcc) and defined(windows): 12 | when defined(x86): 13 | {.link: "icons/nim.res".} 14 | else: 15 | {.link: "icons/nim_icon.o".} 16 | 17 | #[ 18 | ? question 19 | block comment 20 | ]# 21 | 22 | when defined(amd64) and defined(windows) and defined(vcc): 23 | {.link: "icons/nim-amd64-windows-vcc.res".} 24 | when defined(i386) and defined(windows) and defined(vcc): 25 | {.link: "icons/nim-i386-windows-vcc.res".} 26 | 27 | import 28 | commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, 29 | extccomp, strutils, os, osproc, platform, main, parseopt, service, 30 | nodejs, scriptconfig, idents, modulegraphs 31 | 32 | # ? adding some sample comments for better-comments 33 | when hasTinyCBackend: 34 | import tccgen 35 | 36 | when defined(profiler) or defined(memProfiler): 37 | {.hint: "Profiling support is turned on!".} 38 | import nimprof 39 | 40 | proc prependCurDir(f: string): string = 41 | when defined(unix): 42 | if os.isAbsolute(f): result = f 43 | else: result = "./" & f 44 | else: 45 | result = f 46 | 47 | # ! adding some sample comments for better-comments 48 | proc handleCmdLine(cache: IdentCache; config: ConfigRef) = 49 | if paramCount() == 0: 50 | writeCommandLineUsage() 51 | else: 52 | # Process command line arguments: 53 | processCmdLine(passCmd1, "") 54 | if gProjectName == "-": 55 | gProjectName = "stdinfile" 56 | gProjectFull = "stdinfile" 57 | gProjectPath = canonicalizePath getCurrentDir() 58 | gProjectIsStdin = true 59 | elif gProjectName != "": 60 | try: 61 | gProjectFull = canonicalizePath(gProjectName) 62 | except OSError: 63 | gProjectFull = gProjectName 64 | let p = splitFile(gProjectFull) 65 | let dir = if p.dir.len > 0: p.dir else: getCurrentDir() 66 | gProjectPath = canonicalizePath dir 67 | gProjectName = p.name 68 | else: 69 | gProjectPath = canonicalizePath getCurrentDir() 70 | loadConfigs(DefaultConfig, config) # load all config files 71 | let scriptFile = gProjectFull.changeFileExt("nims") 72 | if fileExists(scriptFile): 73 | runNimScript(cache, scriptFile, freshDefines=false, config) 74 | # 'nim foo.nims' means to just run the NimScript file and do nothing more: 75 | if scriptFile == gProjectFull: return 76 | elif fileExists(gProjectPath / "config.nims"): 77 | # directory wide NimScript file 78 | runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config) 79 | # now process command line arguments again, because some options in the 80 | # command line can overwite the config file's settings 81 | extccomp.initVars() 82 | processCmdLine(passCmd2, "") 83 | if options.command == "": 84 | rawMessage(errNoCommand, command) 85 | mainCommand(newModuleGraph(config), cache) 86 | if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics()) 87 | #echo(GC_getStatistics()) 88 | if msgs.gErrorCounter == 0: 89 | when hasTinyCBackend: 90 | if gCmd == cmdRun: 91 | tccgen.run(commands.arguments) 92 | if optRun in gGlobalOptions: 93 | if gCmd == cmdCompileToJS: 94 | var ex: string 95 | if options.outFile.len > 0: 96 | ex = options.outFile.prependCurDir.quoteShell 97 | else: 98 | ex = quoteShell( 99 | completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) 100 | execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments) 101 | elif gCmd == cmdCompileToPHP: 102 | var ex: string 103 | if options.outFile.len > 0: 104 | ex = options.outFile.prependCurDir.quoteShell 105 | else: 106 | ex = quoteShell( 107 | completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir)) 108 | execExternalProgram("php " & ex & ' ' & commands.arguments) 109 | else: 110 | var binPath: string 111 | if options.outFile.len > 0: 112 | # If the user specified an outFile path, use that directly. 113 | binPath = options.outFile.prependCurDir 114 | else: 115 | # Figure out ourselves a valid binary name. 116 | binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir 117 | var ex = quoteShell(binPath) 118 | execExternalProgram(ex & ' ' & commands.arguments) 119 | 120 | # TODO adding some sample comments for better-comments 121 | when declared(GC_setMaxPause): 122 | GC_setMaxPause 2_000 123 | 124 | when compileOption("gc", "v2") or compileOption("gc", "refc"): 125 | # the new correct mark&sweet collector is too slow :-/ 126 | GC_disableMarkAndSweep() 127 | condsyms.initDefines() 128 | 129 | # // adding some sample comments for better-comments 130 | when not defined(selftest): 131 | handleCmdLine(newIdentCache(), newConfigRef()) 132 | when declared(GC_setMaxPause): 133 | echo GC_getStatistics() 134 | msgQuit(int8(msgs.gErrorCounter > 0)) 135 | -------------------------------------------------------------------------------- /samples/nested.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This is my class 3 | ! This class is not for public use 4 | 5 | TODO is multiline comment 6 | ensure continuous lines with indentation. 7 | 8 | * Highlight to draw attention 9 | ? Maybe I should indent less 10 | */ 11 | public class MyClass 12 | { 13 | // ! backing member for the public property 14 | private short myProperty = 0; 15 | 16 | // * Available for public use 17 | public short MyProperty 18 | { 19 | // TODO add some better comments 20 | get { return this.myProperty; } 21 | 22 | // ? should this value be transformed first? 23 | set { this.myProperty = value; } 24 | 25 | } 26 | 27 | public constructor() 28 | { 29 | this.MyProperty = 1; 30 | } 31 | 32 | public void DoSomeStuff(string myParameter) 33 | { 34 | 35 | } 36 | 37 | private boolean doSomePrivateStuff() 38 | { 39 | 40 | } 41 | } 42 | 43 | /* 44 | This is my class 45 | ! This class is not for public use 46 | TODO Create some copyright notices 47 | 48 | * Highlight to draw attention 49 | ? Maybe I should indent less 50 | */ 51 | public class MyClass 52 | { 53 | // ! backing member for the public property 54 | private short myProperty = 0; 55 | 56 | // * Available for public use 57 | public short MyProperty 58 | { 59 | // TODO add some better comments 60 | get { return this.myProperty; } 61 | 62 | // ? should this value be transformed first? 63 | set { this.myProperty = value; } 64 | 65 | } 66 | 67 | public constructor() 68 | { 69 | this.MyProperty = 1; 70 | } 71 | 72 | public void DoSomeStuff(string myParameter) 73 | { 74 | 75 | } 76 | 77 | private boolean doSomePrivateStuff() 78 | { 79 | 80 | } 81 | } 82 | 83 | /* 84 | This is my class 85 | ! This class is not for public use 86 | TODO Create some copyright notices 87 | 88 | * Highlight to draw attention 89 | ? Maybe I should indent less 90 | */ 91 | public class MyClass 92 | { 93 | // ! backing member for the public property 94 | private short myProperty = 0; 95 | 96 | // * Available for public use 97 | public short MyProperty 98 | { 99 | // TODO add some better comments 100 | get { return this.myProperty; } 101 | 102 | // ? should this value be transformed first? 103 | set { this.myProperty = value; } 104 | 105 | } 106 | 107 | public constructor() 108 | { 109 | this.MyProperty = 1; 110 | } 111 | 112 | public void DoSomeStuff(string myParameter) 113 | { 114 | 115 | } 116 | 117 | private boolean doSomePrivateStuff() 118 | { 119 | 120 | } 121 | } 122 | 123 | /* 124 | This is my class 125 | ! This class is not for public use 126 | TODO Create some copyright notices 127 | 128 | * Highlight to draw attention 129 | ? Maybe I should indent less 130 | */ 131 | public class MyClass 132 | { 133 | // ! backing member for the public property 134 | private short myProperty = 0; 135 | 136 | // * Available for public use 137 | public short MyProperty 138 | { 139 | // TODO add some better comments 140 | get { return this.myProperty; } 141 | 142 | // ? should this value be transformed first? 143 | set { this.myProperty = value; } 144 | 145 | } 146 | 147 | public constructor() 148 | { 149 | this.MyProperty = 1; 150 | } 151 | 152 | public void DoSomeStuff(string myParameter) 153 | { 154 | 155 | } 156 | 157 | private boolean doSomePrivateStuff() 158 | { 159 | 160 | } 161 | } 162 | 163 | /* 164 | This is my class 165 | ! This class is not for public use 166 | TODO Create some copyright notices 167 | 168 | * Highlight to draw attention 169 | ? Maybe I should indent less 170 | */ 171 | public class MyClass 172 | { 173 | // ! backing member for the public property 174 | private short myProperty = 0; 175 | 176 | // * Available for public use 177 | public short MyProperty 178 | { 179 | // TODO add some better comments 180 | get { return this.myProperty; } 181 | 182 | // ? should this value be transformed first? 183 | set { this.myProperty = value; } 184 | 185 | } 186 | 187 | public constructor() 188 | { 189 | this.MyProperty = 1; 190 | } 191 | 192 | public void DoSomeStuff(string myParameter) 193 | { 194 | 195 | } 196 | 197 | private boolean doSomePrivateStuff() 198 | { 199 | 200 | } 201 | } 202 | 203 | /* 204 | This is my class 205 | ! This class is not for public use 206 | TODO Create some copyright notices 207 | 208 | * Highlight to draw attention 209 | ? Maybe I should indent less 210 | */ 211 | public class MyClass 212 | { 213 | // ! backing member for the public property 214 | private short myProperty = 0; 215 | 216 | // * Available for public use 217 | public short MyProperty 218 | { 219 | // TODO add some better comments 220 | get { return this.myProperty; } 221 | 222 | // ? should this value be transformed first? 223 | set { this.myProperty = value; } 224 | 225 | } 226 | 227 | public constructor() 228 | { 229 | this.MyProperty = 1; 230 | } 231 | 232 | public void DoSomeStuff(string myParameter) 233 | { 234 | 235 | } 236 | 237 | private boolean doSomePrivateStuff() 238 | { 239 | 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Better Comments Next 2 | 3 | Forked from [aaron-bond/better-comments v3.0.2](https://github.com/aaron-bond/better-comments) 4 | 5 | ## Features 6 | 7 | - [x] Fix matching errors. 8 | - [x] All languages supported. 9 | - [x] Custom comments configuration for languages configurated by [`vscode.languages.setLanguageConfiguration`](https://code.visualstudio.com/api/references/vscode-api#languages) See [#11](https://github.com/edwinhuish/better-comments-next/issues/11) 10 | - [x] Embedded languages supported. Like SFC of Vue, markdown, HTML, etc. See [#388](https://github.com/aaron-bond/better-comments/issues/388#issuecomment-1527426462) 11 | - [x] Remote workspace supported. See [#507](https://github.com/aaron-bond/better-comments/issues/507) 12 | - [x] Web editor supported. 13 | - [x] Theme switchable. Different tag config for light and dark themes. See [#506](https://github.com/aaron-bond/better-comments/issues/506) 14 | - [x] Allow multiple tags per item. See [#33](https://github.com/aaron-bond/better-comments/issues/33) 15 | - [x] Multi-line comment supported. See [#7](https://github.com/edwinhuish/better-comments-next/issues/7#issuecomment-2522526938) 16 | 17 | ## Description 18 | The Better Comments Next extension will help you create more human-friendly comments in your code. 19 | With this extension, you will be able to categorize your annotations into: 20 | 21 | * Alerts 22 | * Queries 23 | * TODOs 24 | * Highlights 25 | * Commented out code can also be styled to make it clear the code shouldn't be there 26 | * Any other comment styles you'd like can be specified in the settings 27 | 28 | ![Annotated code](static/better-comments.png) 29 | 30 | ## Configuration 31 | 32 | Default setting as below: 33 | 34 | ```jsonc 35 | { 36 | // Millisecond delay for update decorations, default 0 37 | "better-comments.updateDelay": 0, 38 | // Preload lines outside the visible window for better performance, default 100 39 | "better-comments.preloadLines": 100, 40 | // Enable/disable highlight plain text. 41 | "better-comments.highlightPlainText": false, 42 | // Highlight entire line of line comment 43 | "better-comments.fullHighlight": false, 44 | // Strict mode of tag matching. Default true 45 | "better-comments.strict": true, 46 | // Custom languages comments configuration 47 | "better-comments.languages": [ 48 | // { 49 | // "id": "proto3", // (Required) Language ID 50 | // "comments": { "lineComment": "//", "blockComment": ["/*", "*/"] }, // (Optional) Comment Syntax 51 | // "embeddedLanguages": [], // (Optional) Embedded Languages. Example for HTML: ["css", "javascript"] 52 | // "useDocComment": false // (Optional) Use Doc Comments 53 | // } 54 | ], 55 | // Overwrite the specified tag styles of `"better-comments.tags"` for light themes. 56 | "better-comments.tagsLight": [], 57 | // Overwrite the specified tag styles of `"better-comments.tags"` for dark themes. 58 | "better-comments.tagsDark": [], 59 | // Tags for decoration. 60 | "better-comments.tags": [ 61 | { 62 | "tag": "#", 63 | "color": "#18b566", 64 | "strikethrough": false, 65 | "underline": false, 66 | "backgroundColor": "transparent", 67 | "bold": true, 68 | "italic": false 69 | }, 70 | { 71 | "tag": "!", 72 | "color": "#FF2D00", 73 | "strikethrough": false, 74 | "underline": false, 75 | "backgroundColor": "transparent", 76 | "bold": false, 77 | "italic": false 78 | }, 79 | { 80 | "tag": "?", 81 | "color": "#3498DB", 82 | "strikethrough": false, 83 | "underline": false, 84 | "backgroundColor": "transparent", 85 | "bold": false, 86 | "italic": false 87 | }, 88 | { 89 | "tag": "//", 90 | "color": "#474747", 91 | "strikethrough": true, 92 | "underline": false, 93 | "backgroundColor": "transparent", 94 | "bold": false, 95 | "italic": false 96 | }, 97 | { 98 | "tag": ["todo", "to-do"], 99 | "color": "#FF8C00", 100 | "strikethrough": false, 101 | "underline": false, 102 | "backgroundColor": "transparent", 103 | "bold": false, 104 | "italic": false, 105 | "multiline": true 106 | }, 107 | { 108 | "tag": "*", 109 | "color": "#98C379", 110 | "strikethrough": false, 111 | "underline": false, 112 | "backgroundColor": "transparent", 113 | "bold": false, 114 | "italic": false 115 | }, 116 | { 117 | "tag": "bug", 118 | "color": "#E84393", 119 | "strikethrough": false, 120 | "underline": true, 121 | "backgroundColor": "#FDA7DF20", 122 | "bold": true, 123 | "italic": false 124 | }, 125 | { 126 | "tag": "hack", 127 | "color": "#9B59B6", 128 | "strikethrough": false, 129 | "underline": false, 130 | "backgroundColor": "#9B59B620", 131 | "bold": true, 132 | "italic": true 133 | }, 134 | { 135 | "tag": [ 136 | "fixme", 137 | "fix-me", 138 | "fix" 139 | ], 140 | "color": "#FD79A8", 141 | "strikethrough": false, 142 | "underline": false, 143 | "backgroundColor": "#FD79A820", 144 | "bold": true, 145 | "italic": false 146 | } 147 | ] 148 | } 149 | ``` 150 | 151 | ### About `strict` mode 152 | 153 | **"better-comments.strict": true** 154 | 155 | ![Config strict: true](static/strict_true.png) 156 | 157 | **"better-comments.strict": false** 158 | 159 | ![Config strict: false](static/strict_false.png) 160 | 161 | ## Supported Languages 162 | 163 | **All languages supported:** 164 | 165 | - Auto detected languages comments rules from extension configuration. 166 | - Manual configured languages comments rules by `"better-comments.languages"`. 167 | -------------------------------------------------------------------------------- /src/configuration/configuration.ts: -------------------------------------------------------------------------------- 1 | import type { WorkspaceConfiguration } from 'vscode'; 2 | import { escape } from '@/utils/regex'; 3 | import * as vscode from 'vscode'; 4 | 5 | export interface Tag { 6 | tag: string | string[]; 7 | color: string; 8 | strikethrough: boolean; 9 | underline: boolean; 10 | bold: boolean; 11 | italic: boolean; 12 | backgroundColor: string; 13 | multiline: boolean; 14 | } 15 | 16 | export interface TagFlatten extends Tag { 17 | tag: string; 18 | tagEscaped: string; 19 | } 20 | 21 | export interface Language { 22 | /** 23 | * The language id 24 | */ 25 | id: string; 26 | 27 | /** 28 | * The language's comment settings. 29 | */ 30 | comments: vscode.CommentRule; 31 | 32 | /** 33 | * Whether the language has doc comment 34 | */ 35 | useDocComment: boolean; 36 | 37 | /** 38 | * The embedded languages ids 39 | */ 40 | embeddedLanguages: string[]; 41 | } 42 | 43 | interface Configuration { 44 | highlightPlainText: boolean; 45 | tags: Tag[]; 46 | tagsLight: Tag[]; 47 | tagsDark: Tag[]; 48 | languages: Language[]; 49 | updateDelay: number; 50 | preloadLines: number; 51 | fullHighlight: boolean; // Highlight entire line of line comment 52 | strict: boolean; 53 | } 54 | 55 | export interface ConfigurationFlatten extends Configuration { 56 | tags: TagFlatten[]; 57 | tagsLight: TagFlatten[]; 58 | tagsDark: TagFlatten[]; 59 | } 60 | 61 | let config: (Configuration & WorkspaceConfiguration) | undefined; 62 | let configFlatten: ConfigurationFlatten | undefined; 63 | let tagDecorationTypes: Map | undefined; 64 | let multilineTagsEscaped: string[] | undefined; 65 | let lineTagsEscaped: string[] | undefined; 66 | let allTagsEscaped: string[] | undefined; 67 | 68 | export function refresh() { 69 | // if already set tagDecorationTypes, clear decoration for visible editors 70 | if (tagDecorationTypes) { 71 | for (const editor of vscode.window.visibleTextEditors) { 72 | for (const [, decorationType] of tagDecorationTypes) { 73 | // clear decoration 74 | editor.setDecorations(decorationType, []); 75 | } 76 | } 77 | } 78 | 79 | config = undefined; 80 | configFlatten = undefined; 81 | tagDecorationTypes = undefined; 82 | multilineTagsEscaped = undefined; 83 | lineTagsEscaped = undefined; 84 | allTagsEscaped = undefined; 85 | } 86 | 87 | /** 88 | * Get better comments configuration 89 | */ 90 | function getConfiguration() { 91 | if (!config) { 92 | config = vscode.workspace.getConfiguration('better-comments') as Configuration & WorkspaceConfiguration; 93 | } 94 | 95 | return config!; 96 | } 97 | 98 | /** 99 | * Get better comments configuration in flatten 100 | */ 101 | export function getConfigurationFlatten() { 102 | if (configFlatten) { 103 | return configFlatten; 104 | } 105 | const orig = getConfiguration(); 106 | 107 | configFlatten = { 108 | ...orig, 109 | tags: flattenTags(orig.tags), 110 | tagsLight: flattenTags(orig.tagsLight), 111 | tagsDark: flattenTags(orig.tagsDark), 112 | }; 113 | 114 | return configFlatten; 115 | } 116 | 117 | /** 118 | * Flatten config tags 119 | */ 120 | function flattenTags(tags: Tag[]) { 121 | const flatTags: TagFlatten[] = []; 122 | for (const tag of tags) { 123 | if (!Array.isArray(tag.tag)) { 124 | // ! add tag only tag name not empty 125 | if (tag.tag) { 126 | flatTags.push({ ...tag, tagEscaped: escape(tag.tag) } as TagFlatten); 127 | } 128 | continue; 129 | } 130 | 131 | for (const tagName of tag.tag) { 132 | // ! add tag only tag name not empty 133 | if (!tagName) { 134 | continue; 135 | } 136 | flatTags.push({ 137 | ...tag, 138 | tag: tagName, 139 | tagEscaped: escape(tagName), 140 | }); 141 | } 142 | } 143 | return flatTags; 144 | } 145 | 146 | export function getTagDecorationTypes() { 147 | if (!tagDecorationTypes) { 148 | const configs = getConfigurationFlatten(); 149 | 150 | tagDecorationTypes = new Map(); 151 | 152 | for (const tag of configs.tags) { 153 | const opt = parseDecorationRenderOption(tag); 154 | 155 | const tagLight = configs.tagsLight.find(t => t.tag === tag.tag); 156 | if (tagLight) { 157 | opt.light = parseDecorationRenderOption(tagLight); 158 | } 159 | 160 | const tagDark = configs.tagsDark.find(t => t.tag === tag.tag); 161 | if (tagDark) { 162 | opt.dark = parseDecorationRenderOption(tagDark); 163 | } 164 | 165 | const tagName = tag.tag.toLowerCase(); 166 | tagDecorationTypes.set(tagName, vscode.window.createTextEditorDecorationType(opt)); 167 | } 168 | } 169 | 170 | return tagDecorationTypes; 171 | } 172 | 173 | /** 174 | * Parse decoration render option by tag configuration 175 | */ 176 | function parseDecorationRenderOption(tag: TagFlatten) { 177 | const options: vscode.DecorationRenderOptions = { color: tag.color, backgroundColor: tag.backgroundColor }; 178 | 179 | const textDecorations: string[] = []; 180 | tag.strikethrough && textDecorations.push('line-through'); 181 | tag.underline && textDecorations.push('underline'); 182 | options.textDecoration = textDecorations.join(' '); 183 | 184 | if (tag.bold) { 185 | options.fontWeight = 'bold'; 186 | } 187 | 188 | if (tag.italic) { 189 | options.fontStyle = 'italic'; 190 | } 191 | 192 | return options; 193 | } 194 | 195 | export function getMultilineTagsEscaped() { 196 | if (!multilineTagsEscaped) { 197 | multilineTagsEscaped = getConfigurationFlatten().tags.filter(t => t.multiline).map(tag => tag.tagEscaped); 198 | } 199 | 200 | return multilineTagsEscaped; 201 | } 202 | 203 | export function getLineTagsEscaped() { 204 | if (!lineTagsEscaped) { 205 | lineTagsEscaped = getConfigurationFlatten().tags.filter(t => !t.multiline).map(tag => tag.tagEscaped); 206 | } 207 | 208 | return lineTagsEscaped; 209 | } 210 | 211 | export function getAllTagsEscaped() { 212 | if (!allTagsEscaped) { 213 | allTagsEscaped = getConfigurationFlatten().tags.map(tag => tag.tagEscaped); 214 | } 215 | 216 | return allTagsEscaped; 217 | } 218 | -------------------------------------------------------------------------------- /src/definition/modules/common.ts: -------------------------------------------------------------------------------- 1 | import * as log from '@/log'; 2 | import { isString } from '@/utils/str'; 3 | import { parse as json5Parse } from 'json5'; 4 | import * as vscode from 'vscode'; 5 | 6 | export interface AvailableComments { 7 | lineComments: string[]; 8 | blockComments: vscode.CharacterPair[]; 9 | } 10 | 11 | export class Language { 12 | public readonly id: string; 13 | protected configurationUri?: vscode.Uri; 14 | protected configuration?: vscode.LanguageConfiguration; 15 | 16 | protected comments?: vscode.CommentRule; 17 | protected embeddedLanguages: Set; 18 | protected availableComments?: AvailableComments; 19 | 20 | protected useDocComment: boolean = true; 21 | 22 | constructor(id: string) { 23 | this.id = id; 24 | 25 | this.embeddedLanguages = new Set(); 26 | } 27 | 28 | /** 29 | * Set configuration uri 30 | */ 31 | public setConfigurationUri(configurationUri?: vscode.Uri) { 32 | this.configurationUri = configurationUri; 33 | return this; 34 | } 35 | 36 | /** 37 | * Check if config uri already setup 38 | */ 39 | public hasConfigurationUri() { 40 | return !!this.configurationUri; 41 | } 42 | 43 | /** 44 | * Get language configuration 45 | */ 46 | public async getConfiguration() { 47 | if (this.configuration) { 48 | return this.configuration; 49 | } 50 | 51 | if (!this.configurationUri) { 52 | return undefined; 53 | } 54 | 55 | try { 56 | // Read file 57 | const raw = await vscode.workspace.fs.readFile(this.configurationUri); 58 | 59 | const content = raw.toString(); 60 | 61 | // use json5, because the config can contains comments 62 | this.configuration = json5Parse(content) as vscode.LanguageConfiguration; 63 | 64 | return this.configuration; 65 | } 66 | catch (error: any) { 67 | log.error(`Parse configuration file ${this.configurationUri.toString()} failed: ${error.message}`); 68 | return undefined; 69 | } 70 | } 71 | 72 | public setComments(comments: vscode.CommentRule) { 73 | this.comments = comments; 74 | return this; 75 | } 76 | 77 | /** 78 | * Get language comments rules 79 | */ 80 | public async getComments(): Promise { 81 | if (!this.comments) { 82 | const config = await this.getConfiguration(); 83 | 84 | this.comments = {}; 85 | 86 | if (config && config.comments) { 87 | const { lineComment, blockComment } = config.comments; 88 | if (isString(lineComment)) { 89 | this.comments.lineComment = lineComment; 90 | } 91 | if (Array.isArray(blockComment) && blockComment.length === 2 && isString(blockComment[0]) && isString(blockComment[1])) { 92 | this.comments.blockComment = blockComment; 93 | } 94 | } 95 | else { 96 | this.comments = getDefaultComments(this.id); 97 | } 98 | } 99 | 100 | return this.comments; 101 | } 102 | 103 | /** 104 | * Add embedded language id 105 | */ 106 | public addEmbeddedLanguage(langId: string) { 107 | this.embeddedLanguages.add(langId); 108 | return this; 109 | } 110 | 111 | /** 112 | * Get embedded language ids 113 | */ 114 | public getEmbeddedLanguages() { 115 | return this.embeddedLanguages; 116 | } 117 | 118 | /** 119 | * Replace embeddedLanguages 120 | */ 121 | public setEmbeddedLanguages(embeddedLanguages: string[] | Set) { 122 | this.embeddedLanguages = new Set(embeddedLanguages); 123 | return this; 124 | } 125 | 126 | /** 127 | * Get avaiable comments 128 | */ 129 | public getAvailableComments() { 130 | return this.availableComments; 131 | } 132 | 133 | /** 134 | * Set avaiable comments 135 | */ 136 | public setAvailableComments(comments: AvailableComments) { 137 | this.availableComments = comments; 138 | if (comments.lineComments.length) { 139 | log.info(`(${this.id}) LINE COMMENTS: ${comments.lineComments.join('、')}`); 140 | } 141 | if (comments.blockComments.length) { 142 | log.info(`(${this.id}) BLOCK COMMENTS: ${comments.blockComments.map(c => `${c[0]} ${c[1]}`).join('、')}`); 143 | } 144 | return this; 145 | } 146 | 147 | public setUseDocComment(useDocComment: boolean) { 148 | this.useDocComment = useDocComment; 149 | return this; 150 | } 151 | 152 | public isUseDocComment() { 153 | return this.useDocComment; 154 | } 155 | } 156 | 157 | export class CommonLanguage extends Language { 158 | // ignore eslint-prettier error 159 | } 160 | 161 | function getDefaultComments(languageCode: string): vscode.CommentRule | undefined { 162 | switch (languageCode) { 163 | case 'asciidoc': 164 | return { lineComment: '//', blockComment: ['////', '////'] }; 165 | case 'apex': 166 | case 'javascript': 167 | case 'javascriptreact': 168 | case 'typescript': 169 | case 'typescriptreact': 170 | case 'al': 171 | case 'c': 172 | case 'cpp': 173 | case 'csharp': 174 | case 'dart': 175 | case 'flax': 176 | case 'fsharp': 177 | case 'go': 178 | case 'groovy': 179 | case 'haxe': 180 | case 'java': 181 | case 'jsonc': 182 | case 'kotlin': 183 | case 'less': 184 | case 'pascal': 185 | case 'objectpascal': 186 | case 'php': 187 | case 'rust': 188 | case 'scala': 189 | case 'sass': 190 | case 'scss': 191 | case 'stylus': 192 | case 'swift': 193 | case 'verilog': 194 | return { lineComment: '//', blockComment: ['/*', '*/'] }; 195 | case 'css': 196 | return { blockComment: ['/*', '*/'] }; 197 | case 'coffeescript': 198 | case 'dockerfile': 199 | case 'gdscript': 200 | case 'graphql': 201 | case 'julia': 202 | case 'makefile': 203 | case 'perl': 204 | case 'perl6': 205 | case 'puppet': 206 | case 'r': 207 | case 'ruby': 208 | case 'shellscript': 209 | case 'tcl': 210 | case 'yaml': 211 | return { lineComment: '#' }; 212 | case 'elixir': 213 | case 'python': 214 | return { lineComment: '#', blockComment: ['"""', '"""'] }; 215 | case 'nim': 216 | return { lineComment: '#', blockComment: ['#[', ']#'] }; 217 | case 'powershell': 218 | return { lineComment: '#', blockComment: ['<#', '#>'] }; 219 | case 'ada': 220 | case 'hive-sql': 221 | case 'pig': 222 | case 'plsql': 223 | case 'sql': 224 | return { lineComment: '--' }; 225 | case 'lua': 226 | return { lineComment: '--', blockComment: ['--[[', ']]'] }; 227 | case 'elm': 228 | case 'haskell': 229 | return { lineComment: '--', blockComment: ['{-', '-}'] }; 230 | case 'vb': 231 | case 'asp': 232 | case 'diagram': // ? PlantUML is recognized as Diagram (diagram) 233 | return { lineComment: '\'' }; 234 | case 'bibtex': 235 | case 'erlang': 236 | case 'latex': 237 | case 'matlab': 238 | return { lineComment: '%' }; 239 | case 'clojure': 240 | case 'elps': 241 | case 'racket': 242 | case 'lisp': 243 | return { lineComment: ';' }; 244 | case 'terraform': 245 | return { lineComment: '#', blockComment: ['/*', '*/'] }; 246 | case 'COBOL': 247 | return { lineComment: '*>' }; 248 | case 'fortran-modern': 249 | return { lineComment: 'c' }; 250 | case 'SAS': 251 | case 'stata': 252 | return { lineComment: '*', blockComment: ['/*', '*/'] }; 253 | case 'html': 254 | case 'xml': 255 | case 'markdown': 256 | case 'vue': 257 | return { blockComment: [''] }; 258 | case 'twig': 259 | return { blockComment: ['{#', '#}'] }; 260 | case 'genstat': 261 | return { lineComment: '\\', blockComment: ['"', '"'] }; 262 | case 'cfml': 263 | return { blockComment: [''] }; 264 | case 'shaderlab': 265 | return { lineComment: '//' }; 266 | case 'razor': 267 | return { blockComment: ['@*', '*@'] }; 268 | default: 269 | return undefined; 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Package and Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | check: 10 | name: 检查并创建版本号 11 | runs-on: ubuntu-latest 12 | outputs: 13 | created: ${{ steps.create_tag.outputs.created }} 14 | version: ${{ steps.package_json.outputs.version }} 15 | steps: 16 | - name: 检出代码 17 | uses: actions/checkout@v4 18 | 19 | - name: 配置 Node.js 20 | uses: actions/setup-node@v4 21 | 22 | - name: 获取所有标签 23 | run: | 24 | git fetch --prune --unshallow 25 | 26 | - name: 读取 package.json 版本号 27 | id: package_json 28 | run: | 29 | version=$(node -p "require('./package.json').version") 30 | echo "Package version is: $version" 31 | echo "version=$version" >> $GITHUB_OUTPUT 32 | 33 | - name: 检查是否有重复的版本 tag 34 | id: check_version 35 | run: | 36 | package_version=v${{ steps.package_json.outputs.version }} 37 | if git tag -l "$package_version" | grep -q "$package_version"; then 38 | echo "::notice::版本 Tag '$package_version' 已存在。" 39 | echo "exists=true" >> $GITHUB_OUTPUT 40 | fi 41 | 42 | - name: 创建版本tag 43 | id: create_tag 44 | if: steps.check_version.outputs.exists != 'true' 45 | run: | 46 | set -e 47 | version_tag=v${{ steps.package_json.outputs.version }} 48 | echo "Creating new tag: $version_tag" 49 | git tag "$version_tag" 50 | git push origin "$version_tag" 51 | echo "created=true" >> $GITHUB_OUTPUT 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | 55 | publish: 56 | name: 构建并发版 57 | runs-on: ubuntu-latest 58 | needs: check 59 | if: needs.check.outputs.created == 'true' 60 | permissions: 61 | contents: write 62 | steps: 63 | - name: 检出代码 64 | uses: actions/checkout@v4 65 | 66 | - name: 配置 Node.js 67 | uses: actions/setup-node@v4 68 | with: 69 | node-version: 20 70 | 71 | - name: 安装 PNPM 72 | uses: pnpm/action-setup@v4 73 | with: 74 | version: latest 75 | run_install: false 76 | 77 | - name: 配置 pnpm store 目录 78 | shell: bash 79 | run: | 80 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 81 | 82 | - name: 配置 pnpm 缓存 83 | uses: actions/cache@v4 84 | with: 85 | path: ${{ env.STORE_PATH }} 86 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 87 | restore-keys: | 88 | ${{ runner.os }}-pnpm-store- 89 | 90 | - name: 安装依赖 91 | run: pnpm i --frozen-lockfile 92 | 93 | - name: 打包扩展 94 | run: npx vsce package 95 | 96 | - name: 获取当前和上一个标签 97 | id: get_tags 98 | run: | 99 | git fetch --prune --unshallow 100 | tags=($(git tag -l --sort=-committerdate)); 101 | current_tag=${tags[0]}; 102 | previous_tag=${tags[1]}; 103 | echo "previous_tag=$previous_tag" >> $GITHUB_OUTPUT 104 | echo "current_tag=$current_tag" >> $GITHUB_OUTPUT 105 | 106 | - name: 提取并分类提交消息 107 | id: extract_commit_messages 108 | run: | 109 | set -e 110 | current_tag="${{ steps.get_tags.outputs.current_tag }}" 111 | previous_tag="${{ steps.get_tags.outputs.previous_tag }}" 112 | if [ -z "$previous_tag" ]; then 113 | commit_messages=$(git log --pretty=format:"%s - by @%an (%h)" "$current_tag" | grep -E 'feat|fix|docs|perf' || true) 114 | else 115 | commit_messages=$(git log --pretty=format:"%s - by @%an (%h)" "$previous_tag".."$current_tag" | grep -E 'feat|fix|docs|perf' || true) 116 | fi 117 | 118 | # 转义 ` 字符 119 | commit_messages=$(echo "$commit_messages" | sed 's/`/\\\`/g') 120 | 121 | # feat_messages=$(echo "$commit_messages" | grep 'feat' || true) 122 | # fix_messages=$(echo "$commit_messages" | grep 'fix' || true) 123 | # docs_messages=$(echo "$commit_messages" | grep 'docs' || true) 124 | # perf_messages=$(echo "$commit_messages" | grep 'perf' || true) 125 | 126 | # feat_messages=("${feat_messages[@]//\`/\\\`}") 127 | # fix_messages=("${fix_messages[@]//\`/\\\`}") 128 | # docs_messages=("${docs_messages[@]//\`/\\\`}") 129 | # perf_messages=("${perf_messages[@]//\`/\\\`}") 130 | 131 | # echo "feat_messages=(${feat_messages[@]})" >> $GITHUB_OUTPUT 132 | # echo "fix_messages=(${fix_messages[@]})" >> $GITHUB_OUTPUT 133 | # echo "docs_messages=(${docs_messages[@]})" >> $GITHUB_OUTPUT 134 | # echo "perf_messages=(${perf_messages[@]})" >> $GITHUB_OUTPUT 135 | 136 | { 137 | echo 'feat_messages<> $GITHUB_OUTPUT 141 | { 142 | echo 'fix_messages<> $GITHUB_OUTPUT 146 | { 147 | echo 'docs_messages<> $GITHUB_OUTPUT 151 | { 152 | echo 'perf_messages<> $GITHUB_OUTPUT 156 | 157 | - name: 获取当前分支名 158 | id: get_branch_name 159 | run: | 160 | branch_name=$(git rev-parse --abbrev-ref HEAD) 161 | echo "branch_name=$branch_name" >> $GITHUB_OUTPUT 162 | 163 | - name: 发版详情 164 | id: generate_release_notes 165 | run: | 166 | # 提取提交消息分类 167 | feat_messages=("${{ steps.extract_commit_messages.outputs.feat_messages }}") 168 | fix_messages=("${{ steps.extract_commit_messages.outputs.fix_messages }}") 169 | docs_messages=("${{ steps.extract_commit_messages.outputs.docs_messages }}") 170 | perf_messages=("${{ steps.extract_commit_messages.outputs.perf_messages }}") 171 | 172 | release_notes="" 173 | 174 | if [[ -n "$feat_messages" ]]; then 175 | release_notes="$release_notes\n### 🚀 Features 新功能: \n" 176 | while IFS= read -r message; do 177 | release_notes="$release_notes\n- $message" 178 | done <<< "$feat_messages" 179 | fi 180 | 181 | if [[ -n "$fix_messages" ]]; then 182 | release_notes="$release_notes\n### 🩹 Fixes 缺陷修复: \n" 183 | while IFS= read -r message; do 184 | release_notes="$release_notes\n- $message" 185 | done <<< "$fix_messages" 186 | fi 187 | 188 | if [[ -n "$docs_messages" ]]; then 189 | release_notes="$release_notes\n### 📖 Documentation 文档: \n" 190 | while IFS= read -r message; do 191 | release_notes="$release_notes\n- $message" 192 | done <<< "$docs_messages" 193 | fi 194 | 195 | if [[ -n "$perf_messages" ]]; then 196 | release_notes="$release_notes\n### 🔥 Performance 性能优化: \n" 197 | while IFS= read -r message; do 198 | release_notes="$release_notes\n- $message" 199 | done <<< "$perf_messages" 200 | fi 201 | 202 | # 转义 ` 字符 203 | release_notes=$(echo "$release_notes" | sed 's/`/\\\`/g') 204 | echo "release_notes=$release_notes" >> $GITHUB_OUTPUT 205 | 206 | - name: 写入生成的发布说明到 changelog.md 207 | run: | 208 | echo -e "${{ steps.generate_release_notes.outputs.release_notes }}" > changelog.md 209 | cat changelog.md 210 | 211 | - name: 引用 changelog.md 创建发版 212 | id: release_tag 213 | uses: ncipollo/release-action@v1.14.0 214 | with: 215 | bodyFile: changelog.md 216 | artifacts: '*.vsix' 217 | tag: ${{ steps.get_tags.outputs.current_tag }} 218 | 219 | - name: 发布扩展到 VSCODE 220 | run: npx vsce publish 221 | env: 222 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 223 | 224 | - name: 发布扩展到 Open VSX 225 | run: npx ovsx publish 226 | env: 227 | OVSX_PAT: ${{ secrets.OVSX_PAT }} 228 | -------------------------------------------------------------------------------- /samples/lisp.lisp: -------------------------------------------------------------------------------- 1 | ;;;; Math Utilities 2 | 3 | ;;; FIB computes the the Fibonacci function in the traditional 4 | ;;; ! recursive way. 5 | 6 | (defun fib (n) 7 | (check-type n integer) 8 | ;; At this point we're sure we have an integer argument. 9 | ;; ! Now we can get down to some serious computation. 10 | (cond ((< n 0) 11 | ;; Hey, this is just supposed to be a simple example. 12 | ;; Did you really expect me to handle the general case? 13 | (error "FIB got ~D as an argument." n)) 14 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 15 | ;; The cheap cases didn't work. 16 | ;; Nothing more to do but recurse. 17 | (t (+ (fib (- n 1)) ;The traditional formula 18 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 19 | 20 | (defun fib (n) 21 | (check-type n integer) 22 | ;; At this point we're sure we have an integer argument. 23 | ;; ! Now we can get down to some serious computation. 24 | (cond ((< n 0) 25 | ;; Hey, this is just supposed to be a simple example. 26 | ;; Did you really expect me to handle the general case? 27 | (error "FIB got ~D as an argument." n)) 28 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 29 | ;; The cheap cases didn't work. 30 | ;; Nothing more to do but recurse. 31 | (t (+ (fib (- n 1)) ;The traditional formula 32 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 33 | 34 | (defun fib (n) 35 | (check-type n integer) 36 | ;; At this point we're sure we have an integer argument. 37 | ;; ! Now we can get down to some serious computation. 38 | (cond ((< n 0) 39 | ;; Hey, this is just supposed to be a simple example. 40 | ;; Did you really expect me to handle the general case? 41 | (error "FIB got ~D as an argument." n)) 42 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 43 | ;; The cheap cases didn't work. 44 | ;; Nothing more to do but recurse. 45 | (t (+ (fib (- n 1)) ;The traditional formula 46 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 47 | 48 | (defun fib (n) 49 | (check-type n integer) 50 | ;; At this point we're sure we have an integer argument. 51 | ;; ! Now we can get down to some serious computation. 52 | (cond ((< n 0) 53 | ;; Hey, this is just supposed to be a simple example. 54 | ;; Did you really expect me to handle the general case? 55 | (error "FIB got ~D as an argument." n)) 56 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 57 | ;; The cheap cases didn't work. 58 | ;; Nothing more to do but recurse. 59 | (t (+ (fib (- n 1)) ;The traditional formula 60 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 61 | 62 | (defun fib (n) 63 | (check-type n integer) 64 | ;; At this point we're sure we have an integer argument. 65 | ;; ! Now we can get down to some serious computation. 66 | (cond ((< n 0) 67 | ;; Hey, this is just supposed to be a simple example. 68 | ;; Did you really expect me to handle the general case? 69 | (error "FIB got ~D as an argument." n)) 70 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 71 | ;; The cheap cases didn't work. 72 | ;; Nothing more to do but recurse. 73 | (t (+ (fib (- n 1)) ;The traditional formula 74 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 75 | 76 | (defun fib (n) 77 | (check-type n integer) 78 | ;; At this point we're sure we have an integer argument. 79 | ;; ! Now we can get down to some serious computation. 80 | (cond ((< n 0) 81 | ;; Hey, this is just supposed to be a simple example. 82 | ;; Did you really expect me to handle the general case? 83 | (error "FIB got ~D as an argument." n)) 84 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 85 | ;; The cheap cases didn't work. 86 | ;; Nothing more to do but recurse. 87 | (t (+ (fib (- n 1)) ;The traditional formula 88 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 89 | (defun fib (n) 90 | (check-type n integer) 91 | ;; At this point we're sure we have an integer argument. 92 | ;; ! Now we can get down to some serious computation. 93 | (cond ((< n 0) 94 | ;; Hey, this is just supposed to be a simple example. 95 | ;; Did you really expect me to handle the general case? 96 | (error "FIB got ~D as an argument." n)) 97 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 98 | ;; The cheap cases didn't work. 99 | ;; Nothing more to do but recurse. 100 | (t (+ (fib (- n 1)) ;The traditional formula 101 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 102 | (defun fib (n) 103 | (check-type n integer) 104 | ;; At this point we're sure we have an integer argument. 105 | ;; ! Now we can get down to some serious computation. 106 | (cond ((< n 0) 107 | ;; Hey, this is just supposed to be a simple example. 108 | ;; Did you really expect me to handle the general case? 109 | (error "FIB got ~D as an argument." n)) 110 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 111 | ;; The cheap cases didn't work. 112 | ;; Nothing more to do but recurse. 113 | (t (+ (fib (- n 1)) ;The traditional formula 114 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 115 | (defun fib (n) 116 | (check-type n integer) 117 | ;; At this point we're sure we have an integer argument. 118 | ;; ! Now we can get down to some serious computation. 119 | (cond ((< n 0) 120 | ;; Hey, this is just supposed to be a simple example. 121 | ;; Did you really expect me to handle the general case? 122 | (error "FIB got ~D as an argument." n)) 123 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 124 | ;; The cheap cases didn't work. 125 | ;; Nothing more to do but recurse. 126 | (t (+ (fib (- n 1)) ;The traditional formula 127 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2].(defun fib (n) 128 | (check-type n integer) 129 | ;; At this point we're sure we have an integer argument. 130 | ;; ! Now we can get down to some serious computation. 131 | (cond ((< n 0) 132 | ;; Hey, this is just supposed to be a simple example. 133 | ;; ? Did you really expect me to handle the general case? 134 | (error "FIB got ~D as an argument." n)) 135 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 136 | ;; The cheap cases didn't work. 137 | ;; Nothing more to do but recurse. 138 | (t (+ (fib (- n 1)) ;The traditional formula 139 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 140 | (defun fib (n) 141 | (check-type n integer) 142 | ;; At this point we're sure we have an integer argument. 143 | ;; ! Now we can get down to some serious computation. 144 | (cond ((< n 0) 145 | ;; Hey, this is just supposed to be a simple example. 146 | ;; Did you really expect me to handle the general case? 147 | (error "FIB got ~D as an argument." n)) 148 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 149 | ;; The cheap cases didn't work. 150 | ;; Nothing more to do but recurse. 151 | (t (+ (fib (- n 1)) ;The traditional formula 152 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2].(defun fib (n) 153 | (check-type n integer) 154 | ;; At this point we're sure we have an integer argument. 155 | ;; ! Now we can get down to some serious computation. 156 | (cond ((< n 0) 157 | ;; Hey, this is just supposed to be a simple example. 158 | ;; Did you really expect me to handle the general case? 159 | (error "FIB got ~D as an argument." n)) 160 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 161 | ;; The cheap cases didn't work. 162 | ;; Nothing more to do but recurse. 163 | (t (+ (fib (- n 1)) ;The traditional formula 164 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. 165 | 166 | 167 | (defun fib (n) 168 | (check-type n integer) 169 | ;; At this point we're sure we have an integer argument. 170 | ;; ! Now we can get down to some serious computation. 171 | (cond ((< n 0) 172 | ;; Hey, this is just supposed to be a simple example. 173 | ;; Did you really expect me to handle the general case? 174 | (error "FIB got ~D as an argument." n)) 175 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 176 | ;; The cheap cases didn't work. 177 | ;; Nothing more to do but recurse. 178 | (t (+ (fib (- n 1)) ;The traditional formula 179 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2].(defun fib (n) 180 | (check-type n integer) 181 | ;; At this point we're sure we have an integer argument. 182 | ;; ! Now we can get down to some serious computation. 183 | (cond ((< n 0) 184 | ;; Hey, this is just supposed to be a simple example. 185 | ;; Did you really expect me to handle the general case? 186 | (error "FIB got ~D as an argument." n)) 187 | ((< n 2) n) ;fib[0]=0 and fib[1]=1 188 | ;; The cheap cases didn't work. 189 | ;; Nothing more to do but recurse. 190 | (t (+ (fib (- n 1)) ;The traditional formula 191 | (fib (- n 2)))))) ; is fib[n-1]+fib[n-2]. -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [3.3.x] 4 | 5 | ### Features 6 | 7 | * Multi line support See [#7](https://github.com/edwinhuish/better-comments-next/issues/7) 8 | * Custom languages comments configuration for languages configurated by [`vscode.languages.setLanguageConfiguration`](https://code.visualstudio.com/api/references/vscode-api#languages) 9 | * Matching only visible lines See [#28](https://github.com/edwinhuish/better-comments-next/issues/28) 10 | 11 | ### Fix 12 | * Fix PHP hash comments [#14](https://github.com/edwinhuish/better-comments-next/issues/14) 13 | * More optimize performances... 14 | 15 | ### House Keeping 16 | * Refactoring code for split different programming languages 17 | 18 | ## [3.2.x] 19 | 20 | ### Features 21 | 22 | * Listen `better-comments` configuration change. See [#8](https://github.com/edwinhuish/better-comments-next/issues/8) 23 | 24 | ### Fix 25 | 26 | * Skip decorate line comment like inside the block comment. 27 | * Fix python decoration. [#4](https://github.com/edwinhuish/better-comments-next/issues/4) 28 | * Wrong matching for block comments. [#9](https://github.com/edwinhuish/better-comments-next/issues/9) 29 | 30 | ## [3.1.x] 31 | 32 | ### Features 33 | 34 | * Add embedded languages support, read comment rules from languages configuration. See [#388](https://github.com/aaron-bond/better-comments/issues/388#issuecomment-1527426462) Now support all languages that your editor correctly recognizes. 35 | * Support remote workspace. See [#507](https://github.com/aaron-bond/better-comments/issues/507) 36 | * Force match tags with one space (eg. only match with "`// *comment`" or "`// * comment`", no longer match "`//* comment`". See [#2](https://github.com/edwinhuish/better-comments-next/issues/2#issuecomment-1835294075)) 37 | * Overwrite tags options for light / dark theme. See [#506](https://github.com/aaron-bond/better-comments/issues/506) 38 | * Allow multiple tags per item. See [#33](https://github.com/aaron-bond/better-comments/issues/33) 39 | 40 | ### House Keeping 41 | * Change class to function 42 | 43 | ## [3.0.2] (2022-07-30) 44 | 45 | ### House Keeping 46 | 47 | * Adding Sponsor link to package.json ([c2b4992](https://github.com/aaron-bond/better-comments/commit/c2b4992)), closes [#409](https://github.com/aaron-bond/better-comments/issues/409) 48 | 49 | ## [3.0.1] (2022-07-30) 50 | 51 | ### Features 52 | 53 | * Enabling Better Comments to run within VSCode for the Web ([3c4a6ac](https://github.com/aaron-bond/better-comments/commit/3c4a6ac)). Massive thanks to _tanhakabir_ 54 | 55 | ## [3.0.0] (2022-04-05) 56 | 57 | ### Features 58 | 59 | * Adding built in support for all languages ([e1373bf](https://github.com/aaron-bond/better-comments/commit/e1373bf)). Massive thanks to _edgardmessias_ 60 | 61 | ### House Keeping 62 | 63 | * Version bumped all dependencies 64 | * Language support is now driven from configuration files. This means that if you have an extension which informs VSCode about a language, Better Comments will know about it too! 65 | * Problems are likely to arise with this change, but it allows a lot more users to benefit from Better Comments without needing an explicit update for the extension adding support. 66 | 67 | __With version 3.0.0 comes the addition of the support button on the Github page for [Better Comments](https://github.com/sponsors/aaron-bond)__ 68 | __If you feel my work on this extension has earned me a coffee, that's the place to do it!__ 69 | 70 | _**Thanks!**_ 71 | 72 | ## [2.1.0] (2020-07-13) 73 | 74 | ### Features 75 | 76 | * Adding Bold, Italic, and Underline ([e41ccc4](https://github.com/aaron-bond/better-comments/commit/e41ccc4)), closes [#50](https://github.com/aaron-bond/better-comments/issues/50). Massive thanks to _Fr33maan_ 77 | * Adding XML support 78 | * Adding BrightScript support 79 | 80 | ### Bug Fixes 81 | 82 | * Fixing Shaderlab support ([f96049f](https://github.com/aaron-bond/better-comments/commit/f96049f)), closes [#245](https://github.com/aaron-bond/better-comments/issues/245) 83 | * Fixing SASS support ([7decffb](https://github.com/aaron-bond/better-comments/commit/7decffb)), closes [#123](https://github.com/aaron-bond/better-comments/issues/123), [#215](ttps://github.com/aaron-bond/better-comments/issues/215) 84 | 85 | ## [2.0.5] (2019-05-15) 86 | 87 | ### Features 88 | 89 | * Adding Markdown support ([54e51fb](https://github.com/aaron-bond/better-comments/commit/54e51fb)), closes [#91](https://github.com/aaron-bond/better-comments/issues/91) 90 | * Adding Apex support ([301644e](https://github.com/aaron-bond/better-comments/commit/301644e)), closes [#143](https://github.com/aaron-bond/better-comments/issues/143) 91 | * Adding GenStat support ([a14e24c](https://github.com/aaron-bond/better-comments/commit/a14e24c)), closes [#149](https://github.com/aaron-bond/better-comments/issues/149) 92 | * Adding ColdFusion support ([9e2a4be](https://github.com/aaron-bond/better-comments/commit/9e2a4be)), closes [#135](https://github.com/aaron-bond/better-comments/issues/135) 93 | 94 | ## [2.0.4] (2019-05-14) 95 | 96 | ### Bug Fixes 97 | 98 | * Fixing Groovy support ([099bcc0](https://github.com/aaron-bond/better-comments/commit/099bcc0)), closes [#150](https://github.com/aaron-bond/better-comments/issues/150) 99 | * Fixing multiline Lua support ([7ca3164](https://github.com/aaron-bond/better-comments/commit/7ca3164)), closes [#151](https://github.com/aaron-bond/better-comments/issues/151) 100 | 101 | ### Features 102 | 103 | * Supporting remote development, closes [#147](https://github.com/aaron-bond/better-comments/issues/147). Thanks _mjbvz_ 104 | * Adding Elm support, merges [#146](https://github.com/aaron-bond/better-comments/pull/146). Thanks _ChristianPredebon_ 105 | 106 | ## [2.0.3] (2018-11-04) 107 | 108 | ### Features 109 | 110 | * Adding Stylus support ([a57ad30](https://github.com/aaron-bond/better-comments/commit/a57ad30)), merges [#112](https://github.com/aaron-bond/better-comments/issues/112). Thanks _vednoc_ 111 | * Adding ASCIIDoc support ([60a5f5f](https://github.com/aaron-bond/better-comments/commit/60a5f5f)), closes [#107](https://github.com/aaron-bond/better-comments/issues/107) 112 | 113 | ## [2.0.2] (2018-10-10) 114 | 115 | ### Bug Fixes 116 | 117 | * Fixing single line CSS comments([469a93f](https://github.com/aaron-bond/better-comments/commit/469a93f)), closes [#109](https://github.com/aaron-bond/better-comments/issues/109) 118 | * Fixing support for multiline Haskell comments ([498016a](https://github.com/aaron-bond/better-comments/commit/498016a)), closes [#102](https://github.com/aaron-bond/better-comments/issues/102) 119 | 120 | ### Features 121 | 122 | * Adding D support ([c6a619c](https://github.com/aaron-bond/better-comments/commit/c6a619c)), closes [#99](https://github.com/aaron-bond/better-comments/issues/99) 123 | 124 | ## [2.0.1] (2018-09-26) 125 | 126 | ### Bug Fixes 127 | 128 | * Fixing issue where JSDoc block comments weren't being detected properly ([7cb9126](https://github.com/aaron-bond/better-comments/commit/7cb9126)), closes [#101](https://github.com/aaron-bond/better-comments/issues/101) 129 | 130 | ## [2.0.0] (2018-09-20) 131 | 132 | ### Features 133 | 134 | * Block comments for lots and lots of languages. Please raise a bug or feature request if I've missed any! 135 | * Added support for HTML 136 | * Added support for Twig 137 | * Added support for Puppet 138 | 139 | ### House Keeping 140 | 141 | * I decided a major version release was appropriate for this one as it's a pretty huge set of changes in terms of how the extension functions 142 | * It's now possible to add block comment formatting for any new languages as required. Sorry it took so long! 143 | 144 | ## [1.3.0] (2018-09-13) 145 | 146 | ### Features 147 | 148 | * Adding support for Bibtex/Biblatex ([d1f06b6](https://github.com/aaron-bond/better-comments/commit/d1f06b6)), thanks to _JavierReyes945_, merges [#96](https://github.com/aaron-bond/better-comments/pull/96) 149 | * Adding support for Verilog HDL ([b368b17](https://github.com/aaron-bond/better-comments/commit/b368b17)), closes [#84](https://github.com/aaron-bond/better-comments/issues/84) 150 | 151 | ### Bug Fixes 152 | 153 | * Fixing multiline comment support for SAS and Stata ([4b40bd9](https://github.com/aaron-bond/better-comments/commit/4b40bd9)), closes [#95](https://github.com/aaron-bond/better-comments/issues/95) 154 | 155 | ## [1.2.9] (2018-09-08) 156 | 157 | ### Features 158 | 159 | * Adding support for PlantUML ([9a446a3](https://github.com/aaron-bond/better-comments/commit/9a446a3)), thanks to _JavierReyes945_, closes [#94](https://github.com/aaron-bond/better-comments/issues/94) 160 | 161 | ## [1.2.8] (2018-09-03) 162 | 163 | ### Features 164 | 165 | * Added support for Tcl ([52e6d35](https://github.com/aaron-bond/better-comments/commit/52e6d35)), closes [#92](https://github.com/aaron-bond/better-comments/issues/92) 166 | 167 | ## [1.2.7] (2018-09-02) 168 | 169 | ### Features 170 | 171 | * Adding support for Flax ([71f6326](https://github.com/aaron-bond/better-comments/commit/71f6326)), merges [#76](https://github.com/aaron-bond/better-comments/issues/76) 172 | * Adding support for multiple languages, closes [#89](https://github.com/aaron-bond/better-comments/issues/89) 173 | * Fortran (modern) ([8762226](https://github.com/aaron-bond/better-comments/commit/8762226)) 174 | * SAS ([145e8d3](https://github.com/aaron-bond/better-comments/commit/145e8d3)) 175 | * STATA ([eb0f367](https://github.com/aaron-bond/better-comments/commit/eb0f367)) 176 | 177 | ### House Keeping 178 | 179 | * Updating README to reflect actual styntax better ([71f9019](https://github.com/aaron-bond/better-comments/commit/71f9019)), merges [#77](https://github.com/aaron-bond/better-comments/issues/77) 180 | * Messed up the incrementing of the version on this one with the gdscript merge so just pushing this as 1.2.7 for convenience 181 | 182 | ## [1.2.5] (2018-06-04) 183 | 184 | ### Features 185 | 186 | * Adding support for COBOL ([7939ca2](https://github.com/aaron-bond/better-comments/commit/7939ca2)), merges [#34](https://github.com/aaron-bond/better-comments/issues/34) 187 | 188 | ### Bug Fixes 189 | 190 | * Fixing plaintext highlight even when setting is false ([7939ca2](https://github.com/aaron-bond/better-comments/commit/7939ca2)), closes [#73](https://github.com/aaron-bond/better-comments/issues/73) 191 | 192 | ## [1.2.4] (2018-05-31) 193 | 194 | ### Features 195 | 196 | * Adding new property for tags: __backgroundColor__ ([3e7a188](https://github.com/aaron-bond/better-comments/commit/3e7a188)), closes [#66](https://github.com/aaron-bond/better-comments/issues/66) 197 | * default: `transparent` 198 | * Adding support for: PlainText ([27ff774](https://github.com/aaron-bond/better-comments/commit/27ff774)), closes [#39](https://github.com/aaron-bond/better-comments/issues/39) 199 | * PlainText support must be turned on in the settings: `highlightPlainText` 200 | * Adding support for: Vue.js ([2b14d2e](https://github.com/aaron-bond/better-comments/commit/2b14d2e)), closes [#71](https://github.com/aaron-bond/better-comments/issues/71) 201 | * Adding support for: nim ([73a55f6](https://github.com/aaron-bond/better-comments/commit/73a55f6)), merges [#68](https://github.com/aaron-bond/better-comments/issues/68) 202 | * Adding support for: HiveQL and Pig ([e1653ef](https://github.com/aaron-bond/better-comments/commit/e1653ef)), merges [#63](https://github.com/aaron-bond/better-comments/issues/63) 203 | 204 | ## [1.2.3] (2018-05-20) 205 | 206 | ### Features 207 | 208 | * Adding support for: Dart ([7490b81](https://github.com/aaron-bond/better-comments/commit/7490b81)), closes [#65](https://github.com/aaron-bond/better-comments/issues/65) 209 | * Adding support for: Matlab ([e35541b](https://github.com/aaron-bond/better-comments/commit/e35541b)), closes [#58](https://github.com/aaron-bond/better-comments/issues/58) 210 | 211 | ### Bug Fixes 212 | 213 | * Fixing support for SCSS ([2b3919f](https://github.com/aaron-bond/better-comments/commit/2b3919f)), closes [#60](https://github.com/aaron-bond/better-comments/issues/60) 214 | * Fixing Python to prevent first line of the file being detected as a comment, 215 | ([438e0a6](https://github.com/aaron-bond/better-comments/commit/438e0a6)), closes [#61](https://github.com/aaron-bond/better-comments/issues/61) 216 | 217 | ## [1.2.2] (2018-04-15) 218 | 219 | ### Features 220 | 221 | * Adding support for: JSON with comments ([6f0b330](https://github.com/aaron-bond/better-comments/commit/6f0b330)), closes [#51](https://github.com/aaron-bond/better-comments/issues/51) 222 | * Adding support for: AL ([de86410](https://github.com/aaron-bond/better-comments/commit/de86410)), closes [#54](https://github.com/aaron-bond/better-comments/issues/54) 223 | * Adding support for: TypeScript React (.tsx) ([e884b37](https://github.com/aaron-bond/better-comments/commit/e884b37)), closes [#56](https://github.com/aaron-bond/better-comments/issues/56) 224 | 225 | ## [1.2.1] (2018-03-20) 226 | 227 | ### Features 228 | 229 | * Adding support for: Terraform ([c5edd8d](https://github.com/aaron-bond/better-comments/commit/c5edd8d)), closes [#48](https://github.com/aaron-bond/better-comments/issues/48) 230 | 231 | ### Bug Fixes 232 | 233 | * Fixing logic to run the decorations properly when switching back from an unsupported language ([756e0e0](https://github.com/aaron-bond/better-comments/commit/756e0e0)), closes [#47](https://github.com/aaron-bond/better-comments/issues/47) 234 | * Fixing decoration of strikethrough on multiline comments to start in the correct location ([c4372e7](https://github.com/aaron-bond/better-comments/commit/c4372e7)), closes [#46](https://github.com/aaron-bond/better-comments/issues/46) 235 | 236 | ## [1.2.0] (2018-03-19) 237 | 238 | ### Features 239 | 240 | * Adding support for: Clojure, Racket, Lisp ([88e0720](https://github.com/aaron-bond/better-comments/commit/88e0720)), merges [#40](https://github.com/aaron-bond/better-comments/pull/40) 241 | * Adding support for: Yaml ([e9f40a0](https://github.com/aaron-bond/better-comments/commit/e9f40a0)), merges [#37](https://github.com/aaron-bond/better-comments/pull/37) 242 | * Adding support for: Pascal ([655f61f](https://github.com/aaron-bond/better-comments/commit/655f61f)), closes [#41](https://github.com/aaron-bond/better-comments/pull/37) 243 | 244 | ### Bug Fixes 245 | 246 | * Fixing crash when unsupported language is opened in the window ([e9f40a0](https://github.com/aaron-bond/better-comments/commit/e9f40a0)), closes [#35](https://github.com/aaron-bond/better-comments/issues/35) 247 | 248 | ## [1.1.9] (2018-02-11) 249 | 250 | ### Features 251 | 252 | * Adding support for Julia ([1b24ce1](https://github.com/aaron-bond/better-comments/commit/1b24ce1)) 253 | 254 | ## [1.1.8] (2018-01-23) 255 | 256 | ### Features 257 | 258 | * Adding support for GraphQL ([bcfcefa](https://github.com/aaron-bond/better-comments/commit/bcfcefa)), closes [#28](https://github.com/aaron-bond/better-comments/issues/28) 259 | 260 | ### Bug Fixes 261 | 262 | * Expanding non-JSDoc block comment detection ([dccd467](https://github.com/aaron-bond/better-comments/commit/dccd467)), closes [#20](https://github.com/aaron-bond/better-comments/issues/20) 263 | 264 | ## [1.1.7] (2018-01-07) 265 | 266 | ### Bug Fixes 267 | 268 | * Fixing comment detection when tabs are used to start the comment ([2f08fb9](https://github.com/aaron-bond/better-comments/commit/2f08fb9)), closes [#25](https://github.com/aaron-bond/better-comments/issues/25), thanks to _bekorn_ 269 | 270 | ## [1.1.6] (2018-01-15) 271 | 272 | ### Features 273 | 274 | * Adding multiple new languages ([586f325](https://github.com/aaron-bond/better-comments/commit/586f325)), thanks to _Jooseppi12_ 275 | 276 | ## [1.1.5] (2018-01-14) 277 | 278 | #### Bug Fixes 279 | 280 | * Fixing multiline comment detection with non-English characters ([deff42b](https://github.com/aaron-bond/better-comments/commit/deff42b)), closes [#24](https://github.com/aaron-bond/better-comments/issues/24) 281 | 282 | ## [1.1.4] (2018-01-04) 283 | 284 | #### Features 285 | 286 | * Adding activation event and new comment type for VB.NET ("vb") ([45199a9](https://github.com/aaron-bond/better-comments/commit/45199a9)), closes [#21](https://github.com/aaron-bond/better-comments/issues/21) 287 | 288 | ## [1.1.3] (2017-12-22) 289 | 290 | #### Features 291 | 292 | * Adding activation event for React ("javascriptreact") ([e54ae83](https://github.com/aaron-bond/better-comments/commit/e54ae83)), closes [#19](https://github.com/aaron-bond/better-comments/issues/19) 293 | 294 | ## [1.1.2] (2017-12-16) 295 | 296 | #### Bug Fixes 297 | 298 | * Fixed wrong delimiter for Lua ([4bb1e2f](https://github.com/aaron-bond/better-comments/commit/4bb1e2f)), closes [#17](https://github.com/aaron-bond/better-comments/issues/17) 299 | 300 | ## [1.1.1] (2017-12-12) : Accidental Increment 301 | 302 | #### Bug Fixes 303 | 304 | * Fixing issue with options configuration ([0a00618](https://github.com/aaron-bond/better-comments/commit/0a00618)), closes [#16](https://github.com/aaron-bond/better-comments/issues/16) 305 | 306 | ## [1.0.0] (2017-12-06) 307 | 308 | #### Bug Fixes 309 | 310 | * Fixing support for JSDoc style block comments ([69a36bf](https://github.com/aaron-bond/better-comments/commit/69a36bf)), closes [#13](https://github.com/aaron-bond/better-comments/issues/13) 311 | 312 | #### Features 313 | 314 | * Adding support for MANY languages ([0e7eab9](https://github.com/aaron-bond/better-comments/commit/0e7eab9d352780bfb303caf090e186c15bdcc77b)), closes [#8](https://github.com/aaron-bond/better-comments/issues/8), [#9](https://github.com/aaron-bond/better-comments/issues/9) 315 | * Adding customisable comment annotation indicators, closes [#11](https://github.com/aaron-bond/better-comments/issues/11) 316 | 317 | ## [0.1.3] (2017-07-17) 318 | 319 | #### Bug Fixes 320 | 321 | * Fixing an issue where multi-line comments would not be detected if preceded by some indentation ([c36821b](https://github.com/aaron-bond/better-comments/commit/c36821b)) 322 | 323 | #### Features 324 | 325 | * Adding language support for `Go` 326 | 327 | Thanks to __pwebly__ for both of these additions :) 328 | 329 | ## [0.1.2] (2017-07-14) 330 | 331 | #### Bug Fixes 332 | 333 | * Fixing issue with `TODO` and `:` in multiline comments ([5f4d049](https://github.com/aaron-bond/better-comments/commit/5f4d049)), closes [#5](https://github.com/aaron-bond/better-comments/issues/5) 334 | 335 | ## [0.1.1] (2017-07-12) 336 | 337 | #### Features 338 | 339 | * Adding language support for `C` (thanks to _TheWhoAreYouPerson_) ([6f3b852](https://github.com/aaron-bond/better-comments/commit/6f3b852)) 340 | * Adding support for multiline comments (special thanks to _kurozael_ for the suggestion and help implementing) ([cc82fca](https://github.com/aaron-bond/better-comments/commit/cc82fca)) 341 | * Also adding contribution point for this: __multilineComments__: set this to false to disable 342 | 343 | ## [0.1.0] (2017-06-15) 344 | 345 | #### Features 346 | 347 | * Adding new comment type contribution point: __highlightColor__ ([07bd22f](https://github.com/aaron-bond/better-comments/commit/07bd22f)) 348 | 349 | #### Bug Fixes 350 | 351 | * Fixing issue where comment format conflicts with linters expecting a space after initial `//` (special thanks to _TobiasBales_) 352 | 353 | #### House Keeping 354 | 355 | * Added TravisCI config to run unit tests on check in ([bd4b7b2](https://github.com/aaron-bond/better-comments/commit/bd4b7b2)) 356 | * Updated README and demo image to show new comment type ([0cbbccb](https://github.com/aaron-bond/better-comments/commit/0cbbccb)) 357 | 358 | ## [0.0.3] (2017-06-09) 359 | 360 | Initial release to VSCode marketplace 361 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "publisher": "edwinhuish", 3 | "name": "better-comments-next", 4 | "displayName": "Better Comments Next", 5 | "version": "3.4.8", 6 | "description": "Improve your code commenting by annotating with alert, informational, TODOs, and more!", 7 | "author": { 8 | "name": "Edwin Xu" 9 | }, 10 | "sponsor": { 11 | "url": "https://github.com/sponsors/edwinhuish" 12 | }, 13 | "license": "MIT", 14 | "homepage": "https://github.com/edwinhuish/better-comments-next/blob/master/README.md", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/edwinhuish/better-comments-next" 18 | }, 19 | "bugs": "https://github.com/edwinhuish/better-comments-next/issues", 20 | "keywords": [ 21 | "highlight", 22 | "color", 23 | "comment", 24 | "comments", 25 | "todo", 26 | "todos", 27 | "todo-list", 28 | "todo-lists", 29 | "todo-tree", 30 | "asciidoc", 31 | "apex", 32 | "javascript", 33 | "javascriptreact", 34 | "typescript", 35 | "typescriptreact", 36 | "al", 37 | "c", 38 | "cpp", 39 | "csharp", 40 | "dart", 41 | "flax", 42 | "fsharp", 43 | "go", 44 | "groovy", 45 | "haxe", 46 | "java", 47 | "jsonc", 48 | "kotlin", 49 | "less", 50 | "pascal", 51 | "objectpascal", 52 | "php", 53 | "rust", 54 | "scala", 55 | "sass", 56 | "scss", 57 | "stylus", 58 | "swift", 59 | "verilog", 60 | "css", 61 | "coffeescript", 62 | "dockerfile", 63 | "gdscript", 64 | "graphql", 65 | "julia", 66 | "makefile", 67 | "perl", 68 | "perl6", 69 | "puppet", 70 | "r", 71 | "ruby", 72 | "shellscript", 73 | "tcl", 74 | "yaml", 75 | "elixir", 76 | "python", 77 | "nim", 78 | "powershell", 79 | "ada", 80 | "hive-sql", 81 | "pig", 82 | "plsql", 83 | "sql", 84 | "lua", 85 | "elm", 86 | "haskell", 87 | "vb", 88 | "asp", 89 | "diagram", 90 | "bibtex", 91 | "erlang", 92 | "latex", 93 | "matlab", 94 | "clojure", 95 | "elps", 96 | "racket", 97 | "lisp", 98 | "terraform", 99 | "COBOL", 100 | "fortran-modern", 101 | "SAS", 102 | "stata", 103 | "html", 104 | "xml", 105 | "markdown", 106 | "vue", 107 | "twig", 108 | "genstat", 109 | "cfml", 110 | "shaderlab", 111 | "razor" 112 | ], 113 | "categories": [ 114 | "Formatters" 115 | ], 116 | "main": "./dist/extension.js", 117 | "icon": "static/icon.png", 118 | "browser": "./dist/extension.web.js", 119 | "engines": { 120 | "vscode": "^1.65.0" 121 | }, 122 | "extensionKind": [ 123 | "workspace" 124 | ], 125 | "activationEvents": [ 126 | "onLanguage" 127 | ], 128 | "galleryBanner": { 129 | "color": "#e3f4ff", 130 | "theme": "light" 131 | }, 132 | "contributes": { 133 | "configuration": { 134 | "title": "Better Comments Next", 135 | "properties": { 136 | "better-comments.highlightPlainText": { 137 | "type": "boolean", 138 | "description": "Whether the plaintext comment highlighter should be active", 139 | "default": false 140 | }, 141 | "better-comments.tags": { 142 | "type": "array", 143 | "description": "Tags which are used to color the comments.", 144 | "items": { 145 | "type": "object", 146 | "title": "tag item", 147 | "properties": { 148 | "tag": { 149 | "type": [ 150 | "string", 151 | "array" 152 | ], 153 | "description": "Name of tag, case insensitive, could be string or string[]", 154 | "items": { 155 | "type": "string" 156 | } 157 | }, 158 | "color": { 159 | "type": "string", 160 | "description": "Font color, eg: #FF2D00" 161 | }, 162 | "strikethrough": { 163 | "type": "boolean", 164 | "description": "Enable text strikethrough." 165 | }, 166 | "underline": { 167 | "type": "boolean", 168 | "description": "Enable text underline." 169 | }, 170 | "backgroundColor": { 171 | "type": "string", 172 | "description": "Background color, eg: transparent" 173 | }, 174 | "bold": { 175 | "type": "boolean", 176 | "description": "Set text font style to bold." 177 | }, 178 | "italic": { 179 | "type": "boolean", 180 | "description": "Set text font style to italic." 181 | }, 182 | "multiline": { 183 | "type": "boolean", 184 | "description": "Enable multiline comments decoration until reach blank line." 185 | } 186 | } 187 | }, 188 | "default": [ 189 | { 190 | "tag": "#", 191 | "color": "#18b566", 192 | "strikethrough": false, 193 | "underline": false, 194 | "backgroundColor": "transparent", 195 | "bold": true, 196 | "italic": false 197 | }, 198 | { 199 | "tag": "!", 200 | "color": "#FF2D00", 201 | "strikethrough": false, 202 | "underline": false, 203 | "backgroundColor": "transparent", 204 | "bold": false, 205 | "italic": false 206 | }, 207 | { 208 | "tag": "?", 209 | "color": "#3498DB", 210 | "strikethrough": false, 211 | "underline": false, 212 | "backgroundColor": "transparent", 213 | "bold": false, 214 | "italic": false 215 | }, 216 | { 217 | "tag": "//", 218 | "color": "#474747", 219 | "strikethrough": true, 220 | "underline": false, 221 | "backgroundColor": "transparent", 222 | "bold": false, 223 | "italic": false 224 | }, 225 | { 226 | "tag": [ 227 | "todo", 228 | "to-do" 229 | ], 230 | "color": "#FF8C00", 231 | "strikethrough": false, 232 | "underline": false, 233 | "backgroundColor": "transparent", 234 | "bold": false, 235 | "italic": false, 236 | "multiline": true 237 | }, 238 | { 239 | "tag": "*", 240 | "color": "#98C379", 241 | "strikethrough": false, 242 | "underline": false, 243 | "backgroundColor": "transparent", 244 | "bold": false, 245 | "italic": false 246 | }, 247 | { 248 | "tag": "bug", 249 | "color": "#E84393", 250 | "strikethrough": false, 251 | "underline": true, 252 | "backgroundColor": "#FDA7DF20", 253 | "bold": true, 254 | "italic": false 255 | }, 256 | { 257 | "tag": "hack", 258 | "color": "#9B59B6", 259 | "strikethrough": false, 260 | "underline": false, 261 | "backgroundColor": "#9B59B620", 262 | "bold": true, 263 | "italic": true 264 | }, 265 | { 266 | "tag": [ 267 | "fixme", 268 | "fix-me", 269 | "fix" 270 | ], 271 | "color": "#FD79A8", 272 | "strikethrough": false, 273 | "underline": false, 274 | "backgroundColor": "#FD79A820", 275 | "bold": true, 276 | "italic": false 277 | } 278 | ] 279 | }, 280 | "better-comments.tagsLight": { 281 | "type": "array", 282 | "description": "Overwrite tags options for light themes", 283 | "items": { 284 | "type": "object", 285 | "title": "tag item", 286 | "properties": { 287 | "tag": { 288 | "type": [ 289 | "string", 290 | "array" 291 | ], 292 | "description": "Name of tag, case insensitive, could be string or string[]", 293 | "items": { 294 | "type": "string" 295 | } 296 | }, 297 | "color": { 298 | "type": "string", 299 | "description": "Font color, eg: #FF2D00" 300 | }, 301 | "strikethrough": { 302 | "type": "boolean", 303 | "description": "Enable text strikethrough." 304 | }, 305 | "underline": { 306 | "type": "boolean", 307 | "description": "Enable text underline." 308 | }, 309 | "backgroundColor": { 310 | "type": "string", 311 | "description": "Background color, eg: transparent" 312 | }, 313 | "bold": { 314 | "type": "boolean", 315 | "description": "Set text font style to bold." 316 | }, 317 | "italic": { 318 | "type": "boolean", 319 | "description": "Set text font style to italic." 320 | } 321 | } 322 | }, 323 | "default": [] 324 | }, 325 | "better-comments.tagsDark": { 326 | "type": "array", 327 | "description": "Overwrite tags options for dark themes", 328 | "items": { 329 | "type": "object", 330 | "title": "tag item", 331 | "properties": { 332 | "tag": { 333 | "type": [ 334 | "string", 335 | "array" 336 | ], 337 | "description": "Name of tag, case insensitive, could be string or string[]", 338 | "items": { 339 | "type": "string" 340 | } 341 | }, 342 | "color": { 343 | "type": "string", 344 | "description": "Font color, eg: #FF2D00" 345 | }, 346 | "strikethrough": { 347 | "type": "boolean", 348 | "description": "Enable text strikethrough." 349 | }, 350 | "underline": { 351 | "type": "boolean", 352 | "description": "Enable text underline." 353 | }, 354 | "backgroundColor": { 355 | "type": "string", 356 | "description": "Background color, eg: transparent" 357 | }, 358 | "bold": { 359 | "type": "boolean", 360 | "description": "Set text font style to bold." 361 | }, 362 | "italic": { 363 | "type": "boolean", 364 | "description": "Set text font style to italic." 365 | } 366 | } 367 | }, 368 | "default": [] 369 | }, 370 | "better-comments.languages": { 371 | "type": "array", 372 | "description": "List of languages that missing comments definition.", 373 | "items": { 374 | "type": "object", 375 | "title": "language item", 376 | "required": [ 377 | "id" 378 | ], 379 | "properties": { 380 | "id": { 381 | "type": "string", 382 | "description": "The language id" 383 | }, 384 | "comments": { 385 | "type": "object", 386 | "description": "The comment definition", 387 | "properties": { 388 | "lineComment": { 389 | "type": "string", 390 | "description": "The line comment token, like `//`", 391 | "examples": [ 392 | "//" 393 | ] 394 | }, 395 | "blockComment": { 396 | "type": "array", 397 | "maxItems": 2, 398 | "minItems": 2, 399 | "description": "The block comment character pair, like `/*` `*/`", 400 | "examples": [ 401 | "/*", 402 | "*/" 403 | ] 404 | } 405 | } 406 | }, 407 | "useDocComment": { 408 | "type": "boolean", 409 | "description": "Whether the language use doc comment", 410 | "default": true 411 | }, 412 | "embeddedLanguages": { 413 | "type": "array", 414 | "description": "List of embedded language ids", 415 | "items": { 416 | "type": "string", 417 | "description": "The embedded language id" 418 | } 419 | } 420 | } 421 | } 422 | }, 423 | "better-comments.updateDelay": { 424 | "type": "number", 425 | "description": "Millisecond delay for update decorations, default 0", 426 | "default": 0 427 | }, 428 | "better-comments.preloadLines": { 429 | "type": "number", 430 | "description": "Preload lines outside the visible window for better performance, default 100", 431 | "default": 100 432 | }, 433 | "better-comments.fullHighlight": { 434 | "type": "boolean", 435 | "description": "Highlight entire line of line comment", 436 | "default": false 437 | }, 438 | "better-comments.strict": { 439 | "type": "boolean", 440 | "description": "Strict mode of tag. default true.", 441 | "default": true 442 | } 443 | } 444 | } 445 | }, 446 | "scripts": { 447 | "clean:dist": "rimraf dist", 448 | "vscode:prepublish": "npm run package", 449 | "package": "npm run clean:dist && npm run lint && npm run build && npm run build-web", 450 | "build": "tsup --config=build/tsup.node.js", 451 | "watch": "tsup --config=build/tsup.node.js --watch", 452 | "build-web": "tsup --config=build/tsup.web.js", 453 | "watch-web": "tsup --config=build/tsup.web.js --watch", 454 | "lint": "npm run lint:type && npm run lint:eslint", 455 | "lint:type": "tsc -p . --noEmit", 456 | "lint:eslint": "eslint --cache --max-warnings 0 . --fix" 457 | }, 458 | "dependencies": { 459 | "json5": "^2.2.3" 460 | }, 461 | "devDependencies": { 462 | "@antfu/eslint-config": "^3.11.2", 463 | "@types/node": "^17.0.23", 464 | "@types/vscode": "^1.65.0", 465 | "eslint": "^9.16.0", 466 | "husky": "^8.0.3", 467 | "lint-staged": "^15.1.0", 468 | "path-browserify": "^1.0.1", 469 | "process": "^0.11.10", 470 | "rimraf": "^5.0.5", 471 | "tsup": "^8.0.2", 472 | "typescript": "^5.5.4", 473 | "typescript-eslint": "^8" 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /src/handler/modules/common.ts: -------------------------------------------------------------------------------- 1 | import * as configuration from '@/configuration'; 2 | import * as definition from '@/definition'; 3 | import { ANY, BR, escape, SP, SP_BR, TAG_SUFFIX } from '@/utils/regex'; 4 | import { CancelError, generateUUID } from '@/utils/utils'; 5 | import * as vscode from 'vscode'; 6 | 7 | export interface UpdateParams { 8 | editor: vscode.TextEditor; 9 | } 10 | 11 | export interface PickParams { 12 | taskID: string; 13 | editor: vscode.TextEditor; 14 | text: string; 15 | offset: number; 16 | tagRanges: Map; 17 | processed: [number, number][]; 18 | } 19 | 20 | export interface CommentSlice { 21 | start: number; 22 | end: number; 23 | comment: string; 24 | } 25 | 26 | export interface LineCommentSlice extends CommentSlice { 27 | mark: string; 28 | } 29 | 30 | export interface BlockCommentSlice extends CommentSlice { 31 | marks: [string, string]; 32 | content: string; 33 | } 34 | 35 | export interface DocCommentSlice extends BlockCommentSlice { 36 | prefix: string; 37 | } 38 | 39 | export abstract class Handler { 40 | public readonly languageId: string; 41 | protected triggerUpdateTimeout?: NodeJS.Timeout = undefined; 42 | protected taskID = ''; 43 | 44 | constructor(languageId: string) { 45 | this.languageId = languageId; 46 | } 47 | 48 | protected abstract updateDecorations(params: UpdateParams): Promise; 49 | 50 | public async triggerUpdateDecorations({ timeout, ...params }: UpdateParams & { timeout: number }) { 51 | if (this.triggerUpdateTimeout) { 52 | clearTimeout(this.triggerUpdateTimeout); 53 | } 54 | 55 | this.triggerUpdateTimeout = setTimeout(async () => { 56 | try { 57 | await this.updateDecorations(params); 58 | } 59 | catch (e: any) { 60 | if (!(e instanceof CancelError)) { 61 | throw e; 62 | } 63 | } 64 | }, timeout); 65 | } 66 | 67 | protected setDecorations(editor: vscode.TextEditor, tagRanges: Map) { 68 | configuration.getTagDecorationTypes().forEach((td, tag) => { 69 | const ranges = tagRanges.get(tag) || []; 70 | 71 | editor.setDecorations(td, ranges); 72 | const documentUri = editor.document.uri.toString(); 73 | for (const visibleEditor of vscode.window.visibleTextEditors) { 74 | if (visibleEditor === editor) { 75 | continue; 76 | } 77 | 78 | if (visibleEditor.document.uri.toString() !== documentUri) { 79 | continue; 80 | } 81 | 82 | visibleEditor.setDecorations(td, ranges); 83 | } 84 | }); 85 | } 86 | 87 | // verify taskID is current task 88 | protected verifyTaskID(taskID: string) { 89 | if (taskID !== this.taskID) { 90 | throw new CancelError('Task canceled'); 91 | } 92 | } 93 | } 94 | 95 | export class CommonHandler extends Handler { 96 | public async updateDecorations(params: UpdateParams): Promise { 97 | const taskID = this.taskID = generateUUID(); 98 | const processed: [number, number][] = []; 99 | const tagRanges = new Map(); 100 | 101 | const { preloadLines, updateDelay } = configuration.getConfigurationFlatten(); 102 | 103 | // # update for visible ranges 104 | for (const visibleRange of params.editor.visibleRanges) { 105 | this.verifyTaskID(taskID); 106 | 107 | const startLineIdx = Math.max(0, visibleRange.start.line - preloadLines); 108 | const startLine = params.editor.document.lineAt(startLineIdx); 109 | const endLineIdx = Math.min(params.editor.document.lineCount - 1, visibleRange.end.line + preloadLines); 110 | const endLine = params.editor.document.lineAt(endLineIdx); 111 | const range = new vscode.Range(startLine.range.start.line, 0, endLine.range.end.line, endLine.range.end.character); 112 | 113 | const text = params.editor.document.getText(range); 114 | const offset = params.editor.document.offsetAt(range.start); 115 | 116 | const pickParams: PickParams = { editor: params.editor, text, offset, tagRanges, taskID, processed }; 117 | 118 | await this.pickDocCommentDecorationOptions(pickParams); 119 | await this.pickBlockCommentDecorationOptions(pickParams); 120 | await this.pickLineCommentDecorationOptions(pickParams); 121 | } 122 | 123 | this.setDecorations(params.editor, tagRanges); 124 | 125 | setTimeout(async () => { 126 | // # update for full text 127 | this.verifyTaskID(taskID); 128 | const text = params.editor.document.getText(); 129 | const pickParams: PickParams = { editor: params.editor, text, offset: 0, tagRanges, taskID, processed }; 130 | await this.pickDocCommentDecorationOptions(pickParams); 131 | await this.pickBlockCommentDecorationOptions(pickParams); 132 | await this.pickLineCommentDecorationOptions(pickParams); 133 | 134 | this.setDecorations(params.editor, tagRanges); 135 | }, updateDelay); 136 | } 137 | 138 | protected async pickLineCommentSlices(params: PickParams): Promise> { 139 | this.verifyTaskID(params.taskID); 140 | 141 | const { lineComments } = await definition.getAvailableComments(params.editor.document.languageId); 142 | if (!lineComments || !lineComments.length) { 143 | return []; 144 | } 145 | 146 | const slices: LineCommentSlice[] = []; 147 | 148 | const marks = lineComments.map(s => `${escape(s)}+`).join('|'); 149 | 150 | const exp = new RegExp(`(?${marks}).*?(?:${BR}${SP}*\\1.*?)*(?:${BR}|$)`, 'g'); 151 | let block: RegExpExecArray | null; 152 | while ((block = exp.exec(params.text))) { 153 | this.verifyTaskID(params.taskID); 154 | 155 | const start = params.offset + block.index; 156 | const end = start + block[0].length; 157 | 158 | if (params.processed.find(([pStart, pEnd]) => pStart <= start && end <= pEnd)) { 159 | // skip if already processed 160 | continue; 161 | } 162 | // store processed range 163 | params.processed.push([start, end]); 164 | 165 | slices.push({ 166 | start, 167 | end, 168 | comment: block[0], 169 | mark: block.groups!.MARK, 170 | }); 171 | } 172 | 173 | return slices; 174 | } 175 | 176 | private async pickLineCommentDecorationOptions(params: PickParams): Promise { 177 | const slices = await this.pickLineCommentSlices(params); 178 | 179 | this.verifyTaskID(params.taskID); 180 | 181 | const multilineTags = configuration.getMultilineTagsEscaped(); 182 | const lineTags = configuration.getLineTagsEscaped(); 183 | const { fullHighlight, strict } = configuration.getConfigurationFlatten(); 184 | 185 | for (const slice of slices) { 186 | this.verifyTaskID(params.taskID); 187 | 188 | const mark = escape(slice.mark); 189 | 190 | const lineProcessed: [number, number][] = []; 191 | if (multilineTags.length) { 192 | const m1Exp = (() => { 193 | const tag = multilineTags.join('|'); 194 | return strict 195 | ? new RegExp(`(?
    ${SP}*${mark}${SP})(?${tag})(?${TAG_SUFFIX}${ANY}*)`, 'gi')
    196 |             : new RegExp(`(?
    ${SP}*${mark}${SP}?)(?${tag})(?${ANY}*)`, 'gi');
    197 |         })();
    198 | 
    199 |         // Find the matched multiline
    200 |         let m1: RegExpExecArray | null;
    201 |         while ((m1 = m1Exp.exec(slice.comment))) {
    202 |           this.verifyTaskID(params.taskID);
    203 | 
    204 |           const m1Start = slice.start + m1.index;
    205 |           const tagName = m1.groups!.TAG.toLowerCase();
    206 | 
    207 |           // exec with remember last reg index, reset m2Exp avoid reg cache
    208 |           const m2Exp = new RegExp(`(?
    ^|\r?\n|${SP}*)(?${mark})(?${SP}*)(?.*)`, 'gim');
    209 | 
    210 |           // Find decoration range
    211 |           let m2: RegExpExecArray | null;
    212 |           while ((m2 = m2Exp.exec(m1[0]))) {
    213 |             this.verifyTaskID(params.taskID);
    214 | 
    215 |             if (!m2.groups!.CONTENT) {
    216 |               if (m2.index >= m1[0].length) {
    217 |                 break; // index 已经移动到最后的位置,跳出循环
    218 |               }
    219 |               continue; // 空行
    220 |             }
    221 | 
    222 |             if (m2.index !== 0 && m2.groups!.SPACE.length <= 1) {
    223 |               m1Exp.lastIndex = m1.index + m2.index - 1;
    224 |               break;
    225 |             }
    226 | 
    227 |             const m2StartSince = m1Start + m2.index;
    228 |             const m2Start = fullHighlight
    229 |               ? m2StartSince + m2.groups!.PRE.length
    230 |               : m2StartSince + m2.groups!.PRE.length + m2.groups!.MARK.length;
    231 |             const m2End = m2StartSince + m2[0].length;
    232 |             // store processed range
    233 |             lineProcessed.push([m2Start, m2End]);
    234 | 
    235 |             const startPos = params.editor.document.positionAt(m2Start);
    236 |             const endPos = params.editor.document.positionAt(m2End);
    237 |             const range = new vscode.Range(startPos, endPos);
    238 | 
    239 |             const opt = params.tagRanges.get(tagName) || [];
    240 |             opt.push(range);
    241 |             params.tagRanges.set(tagName, opt);
    242 |           }
    243 |         }
    244 |       }
    245 | 
    246 |       if (lineTags.length) {
    247 |         const lineExp = strict
    248 |           ? new RegExp(`(?
    (?:^|${SP})${mark}${SP})(?${lineTags.join('|')})(?${TAG_SUFFIX}.*)`, 'gim')
    249 |           : new RegExp(`(?
    (?:^|${SP})${mark}${SP}?)(?${lineTags.join('|')})(?.*)`, 'gim');
    250 | 
    251 |         let line: RegExpExecArray | null | undefined;
    252 |         while ((line = lineExp.exec(slice.comment))) {
    253 |           this.verifyTaskID(params.taskID);
    254 | 
    255 |           const lineStartSince = slice.start + line.index;
    256 |           const lineStart = fullHighlight
    257 |             ? lineStartSince
    258 |             : lineStartSince + line.groups!.PRE.length;
    259 |           const lineEnd = lineStartSince + line[0].length;
    260 | 
    261 |           if (lineProcessed.find(([pStart, pEnd]) => pStart <= lineStart && lineEnd <= pEnd)) {
    262 |             // skip if already processed
    263 |             continue;
    264 |           }
    265 |           // store processed range
    266 |           lineProcessed.push([lineStart, lineEnd]);
    267 | 
    268 |           const startPos = params.editor.document.positionAt(lineStart);
    269 |           const endPos = params.editor.document.positionAt(lineEnd);
    270 |           const range = new vscode.Range(startPos, endPos);
    271 | 
    272 |           const tagName = line.groups!.TAG.toLowerCase();
    273 | 
    274 |           const opt = params.tagRanges.get(tagName) || [];
    275 |           opt.push(range);
    276 |           params.tagRanges.set(tagName, opt);
    277 |         }
    278 |       }
    279 |     }
    280 |   }
    281 | 
    282 |   protected async pickBlockCommentSlices(params: PickParams): Promise> {
    283 |     this.verifyTaskID(params.taskID);
    284 | 
    285 |     const { blockComments } = await definition.getAvailableComments(params.editor.document.languageId);
    286 |     if (!blockComments || !blockComments.length) {
    287 |       return [];
    288 |     }
    289 | 
    290 |     const slices: BlockCommentSlice[] = [];
    291 | 
    292 |     for (const marks of blockComments) {
    293 |       this.verifyTaskID(params.taskID);
    294 | 
    295 |       const markStart = escape(marks[0]);
    296 |       const markEnd = escape(marks[1]);
    297 |       const exp = new RegExp(`(?
    (?:^|${BR})\\s*)(?${markStart})(?${ANY}*?)(?${markEnd})`, 'g');
    298 | 
    299 |       let block: RegExpExecArray | null;
    300 |       while ((block = exp.exec(params.text))) {
    301 |         this.verifyTaskID(params.taskID);
    302 | 
    303 |         const start = params.offset + block.index + block.groups!.PRE.length;
    304 |         const end = params.offset + block.index + block[0].length;
    305 | 
    306 |         if (params.processed.find(([pStart, pEnd]) => pStart <= start && end <= pEnd)) {
    307 |           // skip if already processed
    308 |           continue;
    309 |         }
    310 |         // store processed range
    311 |         params.processed.push([start, end]);
    312 | 
    313 |         slices.push({
    314 |           start,
    315 |           end,
    316 |           comment: block[0],
    317 |           content: block.groups!.CONTENT,
    318 |           marks,
    319 |         });
    320 |       }
    321 |     }
    322 | 
    323 |     return slices;
    324 |   }
    325 | 
    326 |   private async pickBlockCommentDecorationOptions(params: PickParams): Promise {
    327 |     const slices = await this.pickBlockCommentSlices(params);
    328 |     const multilineTags = configuration.getMultilineTagsEscaped();
    329 |     const { strict } = configuration.getConfigurationFlatten();
    330 |     for (const slice of slices) {
    331 |       this.verifyTaskID(params.taskID);
    332 | 
    333 |       let content = slice.content;
    334 |       let contentStart = slice.start + slice.marks[0].length;
    335 | 
    336 |       const pre = escape(slice.marks[0].slice(-1));
    337 |       const suf = escape(slice.marks[1].slice(0, 1));
    338 |       if (!!pre && !!suf) {
    339 |         const trimExp = new RegExp(`^(${pre}*)(${ANY}*)${suf}*$`, 'i');
    340 |         const trimed = trimExp.exec(slice.content);
    341 |         if (!trimed) {
    342 |           continue;
    343 |         }
    344 | 
    345 |         if (!trimed[2].length) {
    346 |           continue;
    347 |         }
    348 | 
    349 |         content = trimed[2];
    350 |         contentStart += trimed[1].length;
    351 |       }
    352 | 
    353 |       const lineProcessed: [number, number][] = [];
    354 | 
    355 |       if (multilineTags.length) {
    356 |         // exec with remember last reg index, reset m2Exp avoid reg cache
    357 |         const m1Exp = (() => {
    358 |           const tag = multilineTags.join('|');
    359 |           return strict
    360 |             ? new RegExp(`(?
    ^(?${SP})|${BR}(?${SP}*))(?${tag})(?${TAG_SUFFIX}${ANY}*)`, 'gi')
    361 |             : new RegExp(`(?
    ^(?${SP}?)|${BR}(?${SP}*))(?${tag})(?${ANY}*)`, 'gi');
    362 |         })();
    363 | 
    364 |         // Find the matched multiline
    365 |         let m1: RegExpExecArray | null;
    366 |         while ((m1 = m1Exp.exec(content))) {
    367 |           this.verifyTaskID(params.taskID);
    368 | 
    369 |           const m1Start = contentStart + m1.index;
    370 |           const tagName = m1.groups!.TAG.toLowerCase();
    371 |           const m1Space = m1.groups!.SPACE1 || m1.groups!.SPACE2 || '';
    372 | 
    373 |           const m2Exp = /(?
    (?:\r?\n|^)(?[ \t]*))(?.*)/g;
    374 | 
    375 |           // Find decoration range
    376 |           let m2: RegExpExecArray | null;
    377 |           while ((m2 = m2Exp.exec(m1[0]))) {
    378 |             this.verifyTaskID(params.taskID);
    379 | 
    380 |             if (!m2.groups!.CONTENT) {
    381 |               if (m2.index >= m1[0].length) {
    382 |                 break; // index 已经移动到最后的位置,跳出循环
    383 |               }
    384 | 
    385 |               continue; // 空行,继续下次匹配
    386 |             }
    387 | 
    388 |             const m2Space = m2.groups!.SPACE || '';
    389 |             if (m2.index !== 0 && m2Space.length <= m1Space.length) {
    390 |               m1Exp.lastIndex = m1.index + m2.index - 1; // 行的缩进比tag的缩进少,跳出遍历,并修改 m1 的 lastIndex
    391 |               break;
    392 |             }
    393 | 
    394 |             const m2StartSince = m1Start + m2.index;
    395 |             const m2Start = m2StartSince + m2.groups!.PRE.length;
    396 |             const m2End = m2StartSince + m2[0].length;
    397 |             // store processed range
    398 |             lineProcessed.push([m2Start, m2End]);
    399 | 
    400 |             const startPos = params.editor.document.positionAt(m2Start);
    401 |             const endPos = params.editor.document.positionAt(m2End);
    402 |             const range = new vscode.Range(startPos, endPos);
    403 | 
    404 |             const opt = params.tagRanges.get(tagName) || [];
    405 |             opt.push(range);
    406 |             params.tagRanges.set(tagName, opt);
    407 |           }
    408 |         }
    409 |       }
    410 | 
    411 |       const lineTags = configuration.getLineTagsEscaped();
    412 |       if (lineTags.length) {
    413 |         const lineExp = strict
    414 |           ? new RegExp(`(?
    ^${SP}|${BR}${SP}*)(?${lineTags.join('|')})(?${TAG_SUFFIX}.*)`, 'gim')
    415 |           : new RegExp(`(?
    ^${SP}?|${BR}${SP}*)(?${lineTags.join('|')})(?.*)`, 'gim');
    416 |         // Find the matched line
    417 |         let line: RegExpExecArray | null;
    418 |         while ((line = lineExp.exec(content))) {
    419 |           this.verifyTaskID(params.taskID);
    420 | 
    421 |           const lineStartSince = contentStart + line.index;
    422 |           const lineStart = lineStartSince + line.groups!.PRE.length;
    423 |           const lineEnd = lineStartSince + line[0].length;
    424 | 
    425 |           if (lineProcessed.find(([pStart, pEnd]) => pStart <= lineStart && lineEnd <= pEnd)) {
    426 |             continue; // skip if already processed
    427 |           }
    428 |           // store processed range
    429 |           lineProcessed.push([lineStart, lineEnd]);
    430 | 
    431 |           const startPos = params.editor.document.positionAt(lineStart);
    432 |           const endPos = params.editor.document.positionAt(lineEnd);
    433 |           const range = new vscode.Range(startPos, endPos);
    434 | 
    435 |           const tagName = line.groups!.TAG.toLowerCase();
    436 | 
    437 |           const opt = params.tagRanges.get(tagName) || [];
    438 |           opt.push(range);
    439 |           params.tagRanges.set(tagName, opt);
    440 |         }
    441 |       }
    442 |     }
    443 |   }
    444 | 
    445 |   protected async pickDocCommentSlices(params: PickParams): Promise> {
    446 |     this.verifyTaskID(params.taskID);
    447 |     const lang = definition.useLanguage(params.editor.document.languageId);
    448 |     if (!lang.isUseDocComment()) {
    449 |       return [];
    450 |     }
    451 | 
    452 |     // const comments = await lang.getComments();
    453 |     // if (comments?.blockComment?.length) {
    454 |     //   prefix = comments.blockComment[0].slice(-1);
    455 |     //   marks = [comments.blockComment[0] + prefix, comments.blockComment[1]];
    456 |     // }
    457 | 
    458 |     const marks: vscode.CharacterPair = ['/**', '*/'];
    459 |     const prefix = '*';
    460 | 
    461 |     const slices: DocCommentSlice[] = [];
    462 | 
    463 |     const markStart = escape(marks[0]);
    464 |     const markEnd = escape(marks[1]);
    465 | 
    466 |     const blockExp = new RegExp(`(?
    (?:^|${BR})${SP}*)(?${markStart})(?${SP_BR}${ANY}*?)(?${markEnd})`, 'g');
    467 | 
    468 |     let block: RegExpExecArray | null;
    469 |     while ((block = blockExp.exec(params.text))) {
    470 |       this.verifyTaskID(params.taskID);
    471 | 
    472 |       const start = params.offset + block.index + block.groups!.PRE.length;
    473 |       const end = params.offset + block.index + block[0].length;
    474 |       if (params.processed.find(([pStart, pEnd]) => pStart <= start && end <= pEnd)) {
    475 |         // skip if already processed
    476 |         continue;
    477 |       }
    478 |       // store processed range
    479 |       params.processed.push([start, end]);
    480 | 
    481 |       slices.push({
    482 |         start,
    483 |         end,
    484 |         marks,
    485 |         prefix,
    486 |         comment: block.groups!.START + block.groups!.CONTENT + block.groups!.END,
    487 |         content: block.groups!.CONTENT,
    488 |       });
    489 |     }
    490 | 
    491 |     return slices;
    492 |   }
    493 | 
    494 |   private async pickDocCommentDecorationOptions(params: PickParams): Promise {
    495 |     const slices = await this.pickDocCommentSlices(params);
    496 |     const lineProcessed: [number, number][] = [];
    497 |     const multilineTags = configuration.getMultilineTagsEscaped();
    498 |     const lineTags = configuration.getLineTagsEscaped();
    499 |     const { strict } = configuration.getConfigurationFlatten();
    500 |     for (const slice of slices) {
    501 |       this.verifyTaskID(params.taskID);
    502 |       const pre = escape(slice.prefix);
    503 | 
    504 |       if (multilineTags.length) {
    505 |         const m1Exp = (() => {
    506 |           const tag = multilineTags.join('|');
    507 |           return strict
    508 |             ? new RegExp(`(?
    ^${SP}|${SP}*${pre}${SP})(?${tag})(?${TAG_SUFFIX}${ANY}*)`, 'gi')
    509 |             : new RegExp(`(?
    ^${SP}?|${SP}*${pre}${SP}?)(?${tag})(?${ANY}*)`, 'gi');
    510 |         })();
    511 |         // Find the matched multiline
    512 |         let m1: RegExpExecArray | null;
    513 |         while ((m1 = m1Exp.exec(slice.content))) {
    514 |           this.verifyTaskID(params.taskID);
    515 | 
    516 |           const m1Start = slice.start + slice.marks[0].length + m1.index;
    517 |           const tagName = m1.groups!.TAG.toLowerCase();
    518 | 
    519 |           // exec with remember last reg index, reset m2Exp avoid reg cache
    520 |           const m2Exp = new RegExp(`(?
    ${SP}*${pre}|^|\r?\n)(?${SP}*)(?.*)`, 'gim');
    521 | 
    522 |           // Find decoration range
    523 |           let m2: RegExpExecArray | null;
    524 |           while ((m2 = m2Exp.exec(m1.groups!.TAG + m1.groups!.CONTENT))) {
    525 |             this.verifyTaskID(params.taskID);
    526 | 
    527 |             if (!m2.groups!.CONTENT) {
    528 |               if (m2.index >= m1[0].length) {
    529 |                 break; // index 已经移动到最后的位置,跳出循环
    530 |               }
    531 | 
    532 |               continue; // 空行
    533 |             }
    534 | 
    535 |             const m2Space = m2.groups!.SPACE || '';
    536 |             if (m2.index !== 0 && m2Space.length <= 1) { // 必须大于1个空格缩进
    537 |               m1Exp.lastIndex = m1.index + m2.index - 1;
    538 |               break;
    539 |             }
    540 | 
    541 |             const m2StartSince = m1Start + m1.groups!.PRE.length + m2.index;
    542 |             const m2Start = m2StartSince + m2.groups!.PRE.length;
    543 |             const m2End = m2StartSince + m2[0].length;
    544 |             // store processed range
    545 |             lineProcessed.push([m2Start, m2End]);
    546 | 
    547 |             const startPos = params.editor.document.positionAt(m2Start);
    548 |             const endPos = params.editor.document.positionAt(m2End);
    549 |             const range = new vscode.Range(startPos, endPos);
    550 | 
    551 |             const opt = params.tagRanges.get(tagName) || [];
    552 |             opt.push(range);
    553 |             params.tagRanges.set(tagName, opt);
    554 |           }
    555 |         }
    556 |       }
    557 | 
    558 |       if (lineTags.length) {
    559 |         const tags = lineTags.join('|');
    560 |         const linePreTag = `(?:(?:${SP}*${BR}${SP}*${pre})|(?:${SP}*${pre}))`;
    561 |         const lineExp = strict
    562 |           ? new RegExp(`(?
    ${linePreTag}${SP})(?${tags})(?${TAG_SUFFIX}.*)`, 'gim')
    563 |           : new RegExp(`(?
    ${linePreTag}${SP}?)(?${tags})(?.*)`, 'gim');
    564 | 
    565 |         // Find the matched line
    566 |         let line: RegExpExecArray | null;
    567 |         while ((line = lineExp.exec(slice.content))) {
    568 |           this.verifyTaskID(params.taskID);
    569 | 
    570 |           const lineStartSince = slice.start + slice.marks[0].length + line.index;
    571 |           const lineStart = lineStartSince + line.groups!.PRE.length;
    572 |           const lineEnd = lineStartSince + line[0].length;
    573 | 
    574 |           if (lineProcessed.find(range => range[0] <= lineStart && lineEnd <= range[1])) {
    575 |             // skip if already processed
    576 |             continue;
    577 |           }
    578 |           // store processed range
    579 |           lineProcessed.push([lineStart, lineEnd]);
    580 | 
    581 |           const startPos = params.editor.document.positionAt(lineStart);
    582 |           const endPos = params.editor.document.positionAt(lineEnd);
    583 |           const range = new vscode.Range(startPos, endPos);
    584 | 
    585 |           const tagName = line.groups!.TAG.toLowerCase();
    586 | 
    587 |           const opt = params.tagRanges.get(tagName) || [];
    588 |           opt.push(range);
    589 |           params.tagRanges.set(tagName, opt);
    590 |         }
    591 |       }
    592 |     }
    593 |   }
    594 | }
    595 | 
    
    
    --------------------------------------------------------------------------------
    /samples/clojure.clj:
    --------------------------------------------------------------------------------
      1 | ; Inspired by the snakes the have gone before:
      2 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
      3 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
      4 | 
      5 | (ns examples.atom-snake
      6 |   (:import (java.awt Color Dimension) 
      7 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
      8 |            (java.awt.event ActionListener KeyListener))
      9 |   (:use examples.import-static))
     10 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
     11 | 
     12 | ; ----------------------------------------------------------
     13 | ; functional model
     14 | ; ----------------------------------------------------------
     15 | (def width 75)
     16 | (def height 50)
     17 | (def point-size 10)
     18 | (def turn-millis 75)
     19 | (def win-length 5)
     20 | (def dirs { VK_LEFT  [-1  0] 
     21 |             VK_RIGHT [ 1  0]
     22 |             VK_UP    [ 0 -1] 
     23 | 	    VK_DOWN  [ 0  1]})
     24 | 
     25 | (defn add-points [& pts] 
     26 |   (vec (apply map + pts)))
     27 | 
     28 | (defn point-to-screen-rect [pt] 
     29 |   (map #(* point-size %) 
     30 |        [(pt 0) (pt 1) 1 1]))
     31 | 
     32 | (defn create-apple [] 
     33 |   {:location [(rand-int width) (rand-int height)]
     34 |    :color (Color. 210 50 90)
     35 |    :type :apple}) 
     36 | 
     37 | (defn create-snake []
     38 |   {:body (list [1 1]) 
     39 |    :dir [1 0]
     40 |    :type :snake
     41 |    :color (Color. 15 160 70)})
     42 | 
     43 | (defn move [{:keys [body dir] :as snake} & grow]
     44 |   (assoc snake :body (cons (add-points (first body) dir) 
     45 | 			   (if grow body (butlast body)))))
     46 | 
     47 | (defn turn [snake newdir] 
     48 |   (if newdir (assoc snake :dir newdir) snake))
     49 | 
     50 | (defn win? [{body :body}]
     51 |   (>= (count body) win-length))
     52 | 
     53 | (defn head-overlaps-body? [{[head & body] :body}]
     54 |   (contains? (set body) head))
     55 | 
     56 | (def lose? head-overlaps-body?)
     57 | 
     58 | (defn eats? [{[snake-head] :body} {apple :location}]
     59 |    (= snake-head apple))
     60 | 
     61 | ; START: update-positions
     62 | (defn update-positions [{snake :snake, apple :apple, :as game}]
     63 |   (if (eats? snake apple)
     64 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
     65 |     (merge game {:snake (move snake)})))
     66 | ; END: update-positions
     67 | 
     68 | (defn update-direction [{snake :snake :as game} newdir]
     69 |   (merge game {:snake (turn snake newdir)}))
     70 | 
     71 | (defn reset-game [game]
     72 |   (merge game {:apple (create-apple) :snake (create-snake)}))
     73 | 
     74 | ; ----------------------------------------------------------
     75 | ; * * gui * *
     76 | ; ----------------------------------------------------------
     77 | (defn fill-point [g pt color] 
     78 |   (let [[x y width height] (point-to-screen-rect pt)]
     79 |     (.setColor g color) 
     80 |     (.fillRect g x y width height)))
     81 | 
     82 | (defmulti paint (fn [g object & _] (:type object)))
     83 | 
     84 | (defmethod paint :apple [g {:keys [location color]}] 
     85 |   (fill-point g location color))
     86 | 
     87 | (defmethod paint :snake [g {:keys [body color]}] 
     88 |   (doseq [point body]
     89 |     (fill-point g point color)))
     90 | 
     91 | (defn game-panel [frame game]
     92 |   (proxy [JPanel ActionListener KeyListener] []
     93 |     (paintComponent [g] 
     94 |       (proxy-super paintComponent g)
     95 |       (paint g (@game :snake))
     96 |       (paint g (@game :apple)))
     97 |     ; START: swap!
     98 |     (actionPerformed [e] 
     99 |       (swap! game update-positions)
    100 |       (when (lose? (@game :snake))
    101 | 	(swap! game reset-game)
    102 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    103 |     ; END: swap!
    104 |       (when (win? (@game :snake))
    105 | 	(swap! game reset-game)
    106 | 	(JOptionPane/showMessageDialog frame "You win!"))
    107 |       (.repaint this))
    108 |     (keyPressed [e] 
    109 |       (swap! game update-direction (dirs (.getKeyCode e))))
    110 |     (getPreferredSize [] 
    111 |       (Dimension. (* (inc width) point-size) 
    112 | 		  (* (inc height) point-size)))
    113 |     (keyReleased [e])
    114 |     (keyTyped [e])))
    115 | ;;;;; ! hello world
    116 | (defn game [] 
    117 |   (let [game (atom (reset-game {}))
    118 | 	frame (JFrame. "Snake")
    119 | 	panel (game-panel frame game)
    120 | 	timer (Timer. turn-millis panel)]
    121 |     (doto panel 
    122 |       (.setFocusable true)
    123 |       (.addKeyListener panel))
    124 |     (doto frame 
    125 |       (.add panel)
    126 |       (.pack)
    127 |       (.setVisible true))
    128 |     (.start timer) 
    129 | [game, timer])) 
    130 | 
    131 | ; Inspired by the snakes the have gone before:
    132 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
    133 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
    134 | 
    135 | (ns examples.atom-snake
    136 |   (:import (java.awt Color Dimension) 
    137 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
    138 |            (java.awt.event ActionListener KeyListener))
    139 |   (:use examples.import-static))
    140 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
    141 | 
    142 | ; ----------------------------------------------------------
    143 | ; functional model
    144 | ; ----------------------------------------------------------
    145 | (def width 75)
    146 | (def height 50)
    147 | (def point-size 10)
    148 | (def turn-millis 75)
    149 | (def win-length 5)
    150 | (def dirs { VK_LEFT  [-1  0] 
    151 |             VK_RIGHT [ 1  0]
    152 |             VK_UP    [ 0 -1] 
    153 | 	    VK_DOWN  [ 0  1]})
    154 | 
    155 | (defn add-points [& pts] 
    156 |   (vec (apply map + pts)))
    157 | 
    158 | (defn point-to-screen-rect [pt] 
    159 |   (map #(* point-size %) 
    160 |        [(pt 0) (pt 1) 1 1]))
    161 | 
    162 | (defn create-apple [] 
    163 |   {:location [(rand-int width) (rand-int height)]
    164 |    :color (Color. 210 50 90)
    165 |    :type :apple}) 
    166 | 
    167 | (defn create-snake []
    168 |   {:body (list [1 1]) 
    169 |    :dir [1 0]
    170 |    :type :snake
    171 |    :color (Color. 15 160 70)})
    172 | 
    173 | (defn move [{:keys [body dir] :as snake} & grow]
    174 |   (assoc snake :body (cons (add-points (first body) dir) 
    175 | 			   (if grow body (butlast body)))))
    176 | 
    177 | (defn turn [snake newdir] 
    178 |   (if newdir (assoc snake :dir newdir) snake))
    179 | 
    180 | (defn win? [{body :body}]
    181 |   (>= (count body) win-length))
    182 | 
    183 | (defn head-overlaps-body? [{[head & body] :body}]
    184 |   (contains? (set body) head))
    185 | 
    186 | (def lose? head-overlaps-body?)
    187 | 
    188 | (defn eats? [{[snake-head] :body} {apple :location}]
    189 |    (= snake-head apple))
    190 | 
    191 | ; START: update-positions
    192 | (defn update-positions [{snake :snake, apple :apple, :as game}]
    193 |   (if (eats? snake apple)
    194 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
    195 |     (merge game {:snake (move snake)})))
    196 | ; END: update-positions
    197 | 
    198 | (defn update-direction [{snake :snake :as game} newdir]
    199 |   (merge game {:snake (turn snake newdir)}))
    200 | 
    201 | (defn reset-game [game]
    202 |   (merge game {:apple (create-apple) :snake (create-snake)}))
    203 | 
    204 | ; ----------------------------------------------------------
    205 | ; * * gui * *
    206 | ; ----------------------------------------------------------
    207 | (defn fill-point [g pt color] 
    208 |   (let [[x y width height] (point-to-screen-rect pt)]
    209 |     (.setColor g color) 
    210 |     (.fillRect g x y width height)))
    211 | 
    212 | (defmulti paint (fn [g object & _] (:type object)))
    213 | 
    214 | (defmethod paint :apple [g {:keys [location color]}] 
    215 |   (fill-point g location color))
    216 | 
    217 | (defmethod paint :snake [g {:keys [body color]}] 
    218 |   (doseq [point body]
    219 |     (fill-point g point color)))
    220 | 
    221 | (defn game-panel [frame game]
    222 |   (proxy [JPanel ActionListener KeyListener] []
    223 |     (paintComponent [g] 
    224 |       (proxy-super paintComponent g)
    225 |       (paint g (@game :snake))
    226 |       (paint g (@game :apple)))
    227 |     ; START: swap!
    228 |     (actionPerformed [e] 
    229 |       (swap! game update-positions)
    230 |       (when (lose? (@game :snake))
    231 | 	(swap! game reset-game)
    232 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    233 |     ; END: swap!
    234 |       (when (win? (@game :snake))
    235 | 	(swap! game reset-game)
    236 | 	(JOptionPane/showMessageDialog frame "You win!"))
    237 |       (.repaint this))
    238 |     (keyPressed [e] 
    239 |       (swap! game update-direction (dirs (.getKeyCode e))))
    240 |     (getPreferredSize [] 
    241 |       (Dimension. (* (inc width) point-size) 
    242 | 		  (* (inc height) point-size)))
    243 |     (keyReleased [e])
    244 |     (keyTyped [e])))
    245 | ;;;;; ! hello world
    246 | (defn game [] 
    247 |   (let [game (atom (reset-game {}))
    248 | 	frame (JFrame. "Snake")
    249 | 	panel (game-panel frame game)
    250 | 	timer (Timer. turn-millis panel)]
    251 |     (doto panel 
    252 |       (.setFocusable true)
    253 |       (.addKeyListener panel))
    254 |     (doto frame 
    255 |       (.add panel)
    256 |       (.pack)
    257 |       (.setVisible true))
    258 |     (.start timer) 
    259 | [game, timer])) 
    260 | 
    261 | 
    262 | 
    263 | ; Inspired by the snakes the have gone before:
    264 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
    265 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
    266 | 
    267 | (ns examples.atom-snake
    268 |   (:import (java.awt Color Dimension) 
    269 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
    270 |            (java.awt.event ActionListener KeyListener))
    271 |   (:use examples.import-static))
    272 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
    273 | 
    274 | ; ----------------------------------------------------------
    275 | ; functional model
    276 | ; ----------------------------------------------------------
    277 | (def width 75)
    278 | (def height 50)
    279 | (def point-size 10)
    280 | (def turn-millis 75)
    281 | (def win-length 5)
    282 | (def dirs { VK_LEFT  [-1  0] 
    283 |             VK_RIGHT [ 1  0]
    284 |             VK_UP    [ 0 -1] 
    285 | 	    VK_DOWN  [ 0  1]})
    286 | 
    287 | (defn add-points [& pts] 
    288 |   (vec (apply map + pts)))
    289 | 
    290 | (defn point-to-screen-rect [pt] 
    291 |   (map #(* point-size %) 
    292 |        [(pt 0) (pt 1) 1 1]))
    293 | 
    294 | (defn create-apple [] 
    295 |   {:location [(rand-int width) (rand-int height)]
    296 |    :color (Color. 210 50 90)
    297 |    :type :apple}) 
    298 | 
    299 | (defn create-snake []
    300 |   {:body (list [1 1]) 
    301 |    :dir [1 0]
    302 |    :type :snake
    303 |    :color (Color. 15 160 70)})
    304 | 
    305 | (defn move [{:keys [body dir] :as snake} & grow]
    306 |   (assoc snake :body (cons (add-points (first body) dir) 
    307 | 			   (if grow body (butlast body)))))
    308 | 
    309 | (defn turn [snake newdir] 
    310 |   (if newdir (assoc snake :dir newdir) snake))
    311 | 
    312 | (defn win? [{body :body}]
    313 |   (>= (count body) win-length))
    314 | 
    315 | (defn head-overlaps-body? [{[head & body] :body}]
    316 |   (contains? (set body) head))
    317 | 
    318 | (def lose? head-overlaps-body?)
    319 | 
    320 | (defn eats? [{[snake-head] :body} {apple :location}]
    321 |    (= snake-head apple))
    322 | 
    323 | ; START: update-positions
    324 | (defn update-positions [{snake :snake, apple :apple, :as game}]
    325 |   (if (eats? snake apple)
    326 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
    327 |     (merge game {:snake (move snake)})))
    328 | ; END: update-positions
    329 | 
    330 | (defn update-direction [{snake :snake :as game} newdir]
    331 |   (merge game {:snake (turn snake newdir)}))
    332 | 
    333 | (defn reset-game [game]
    334 |   (merge game {:apple (create-apple) :snake (create-snake)}))
    335 | 
    336 | ; ----------------------------------------------------------
    337 | ; * * gui * *
    338 | ; ----------------------------------------------------------
    339 | (defn fill-point [g pt color] 
    340 |   (let [[x y width height] (point-to-screen-rect pt)]
    341 |     (.setColor g color) 
    342 |     (.fillRect g x y width height)))
    343 | 
    344 | (defmulti paint (fn [g object & _] (:type object)))
    345 | 
    346 | (defmethod paint :apple [g {:keys [location color]}] 
    347 |   (fill-point g location color))
    348 | 
    349 | (defmethod paint :snake [g {:keys [body color]}] 
    350 |   (doseq [point body]
    351 |     (fill-point g point color)))
    352 | 
    353 | (defn game-panel [frame game]
    354 |   (proxy [JPanel ActionListener KeyListener] []
    355 |     (paintComponent [g] 
    356 |       (proxy-super paintComponent g)
    357 |       (paint g (@game :snake))
    358 |       (paint g (@game :apple)))
    359 |     ; START: swap!
    360 |     (actionPerformed [e] 
    361 |       (swap! game update-positions)
    362 |       (when (lose? (@game :snake))
    363 | 	(swap! game reset-game)
    364 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    365 |     ; END: swap!
    366 |       (when (win? (@game :snake))
    367 | 	(swap! game reset-game)
    368 | 	(JOptionPane/showMessageDialog frame "You win!"))
    369 |       (.repaint this))
    370 |     (keyPressed [e] 
    371 |       (swap! game update-direction (dirs (.getKeyCode e))))
    372 |     (getPreferredSize [] 
    373 |       (Dimension. (* (inc width) point-size) 
    374 | 		  (* (inc height) point-size)))
    375 |     (keyReleased [e])
    376 |     (keyTyped [e])))
    377 | ;;;;; ! hello world
    378 | (defn game [] 
    379 |   (let [game (atom (reset-game {}))
    380 | 	frame (JFrame. "Snake")
    381 | 	panel (game-panel frame game)
    382 | 	timer (Timer. turn-millis panel)]
    383 |     (doto panel 
    384 |       (.setFocusable true)
    385 |       (.addKeyListener panel))
    386 |     (doto frame 
    387 |       (.add panel)
    388 |       (.pack)
    389 |       (.setVisible true))
    390 |     (.start timer) 
    391 | [game, timer])) 
    392 | 
    393 | ; Inspired by the snakes the have gone before:
    394 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
    395 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
    396 | 
    397 | (ns examples.atom-snake
    398 |   (:import (java.awt Color Dimension) 
    399 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
    400 |            (java.awt.event ActionListener KeyListener))
    401 |   (:use examples.import-static))
    402 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
    403 | 
    404 | ; ----------------------------------------------------------
    405 | ; functional model
    406 | ; ----------------------------------------------------------
    407 | (def width 75)
    408 | (def height 50)
    409 | (def point-size 10)
    410 | (def turn-millis 75)
    411 | (def win-length 5)
    412 | (def dirs { VK_LEFT  [-1  0] 
    413 |             VK_RIGHT [ 1  0]
    414 |             VK_UP    [ 0 -1] 
    415 | 	    VK_DOWN  [ 0  1]})
    416 | 
    417 | (defn add-points [& pts] 
    418 |   (vec (apply map + pts)))
    419 | 
    420 | (defn point-to-screen-rect [pt] 
    421 |   (map #(* point-size %) 
    422 |        [(pt 0) (pt 1) 1 1]))
    423 | 
    424 | (defn create-apple [] 
    425 |   {:location [(rand-int width) (rand-int height)]
    426 |    :color (Color. 210 50 90)
    427 |    :type :apple}) 
    428 | 
    429 | (defn create-snake []
    430 |   {:body (list [1 1]) 
    431 |    :dir [1 0]
    432 |    :type :snake
    433 |    :color (Color. 15 160 70)})
    434 | 
    435 | (defn move [{:keys [body dir] :as snake} & grow]
    436 |   (assoc snake :body (cons (add-points (first body) dir) 
    437 | 			   (if grow body (butlast body)))))
    438 | 
    439 | (defn turn [snake newdir] 
    440 |   (if newdir (assoc snake :dir newdir) snake))
    441 | 
    442 | (defn win? [{body :body}]
    443 |   (>= (count body) win-length))
    444 | 
    445 | (defn head-overlaps-body? [{[head & body] :body}]
    446 |   (contains? (set body) head))
    447 | 
    448 | (def lose? head-overlaps-body?)
    449 | 
    450 | (defn eats? [{[snake-head] :body} {apple :location}]
    451 |    (= snake-head apple))
    452 | 
    453 | ; START: update-positions
    454 | (defn update-positions [{snake :snake, apple :apple, :as game}]
    455 |   (if (eats? snake apple)
    456 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
    457 |     (merge game {:snake (move snake)})))
    458 | ; END: update-positions
    459 | 
    460 | (defn update-direction [{snake :snake :as game} newdir]
    461 |   (merge game {:snake (turn snake newdir)}))
    462 | 
    463 | (defn reset-game [game]
    464 |   (merge game {:apple (create-apple) :snake (create-snake)}))
    465 | 
    466 | ; ----------------------------------------------------------
    467 | ; * * gui * *
    468 | ; ----------------------------------------------------------
    469 | (defn fill-point [g pt color] 
    470 |   (let [[x y width height] (point-to-screen-rect pt)]
    471 |     (.setColor g color) 
    472 |     (.fillRect g x y width height)))
    473 | 
    474 | (defmulti paint (fn [g object & _] (:type object)))
    475 | 
    476 | (defmethod paint :apple [g {:keys [location color]}] 
    477 |   (fill-point g location color))
    478 | 
    479 | (defmethod paint :snake [g {:keys [body color]}] 
    480 |   (doseq [point body]
    481 |     (fill-point g point color)))
    482 | 
    483 | (defn game-panel [frame game]
    484 |   (proxy [JPanel ActionListener KeyListener] []
    485 |     (paintComponent [g] 
    486 |       (proxy-super paintComponent g)
    487 |       (paint g (@game :snake))
    488 |       (paint g (@game :apple)))
    489 |     ; START: swap!
    490 |     (actionPerformed [e] 
    491 |       (swap! game update-positions)
    492 |       (when (lose? (@game :snake))
    493 | 	(swap! game reset-game)
    494 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    495 |     ; END: swap!
    496 |       (when (win? (@game :snake))
    497 | 	(swap! game reset-game)
    498 | 	(JOptionPane/showMessageDialog frame "You win!"))
    499 |       (.repaint this))
    500 |     (keyPressed [e] 
    501 |       (swap! game update-direction (dirs (.getKeyCode e))))
    502 |     (getPreferredSize [] 
    503 |       (Dimension. (* (inc width) point-size) 
    504 | 		  (* (inc height) point-size)))
    505 |     (keyReleased [e])
    506 |     (keyTyped [e])))
    507 | ;;;;; ! hello world
    508 | (defn game [] 
    509 |   (let [game (atom (reset-game {}))
    510 | 	frame (JFrame. "Snake")
    511 | 	panel (game-panel frame game)
    512 | 	timer (Timer. turn-millis panel)]
    513 |     (doto panel 
    514 |       (.setFocusable true)
    515 |       (.addKeyListener panel))
    516 |     (doto frame 
    517 |       (.add panel)
    518 |       (.pack)
    519 |       (.setVisible true))
    520 |     (.start timer) 
    521 | [game, timer])) 
    522 | 
    523 | 
    524 | 
    525 | ; Inspired by the snakes the have gone before:
    526 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
    527 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
    528 | 
    529 | (ns examples.atom-snake
    530 |   (:import (java.awt Color Dimension) 
    531 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
    532 |            (java.awt.event ActionListener KeyListener))
    533 |   (:use examples.import-static))
    534 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
    535 | 
    536 | ; ----------------------------------------------------------
    537 | ; functional model
    538 | ; ----------------------------------------------------------
    539 | (def width 75)
    540 | (def height 50)
    541 | (def point-size 10)
    542 | (def turn-millis 75)
    543 | (def win-length 5)
    544 | (def dirs { VK_LEFT  [-1  0] 
    545 |             VK_RIGHT [ 1  0]
    546 |             VK_UP    [ 0 -1] 
    547 | 	    VK_DOWN  [ 0  1]})
    548 | 
    549 | (defn add-points [& pts] 
    550 |   (vec (apply map + pts)))
    551 | 
    552 | (defn point-to-screen-rect [pt] 
    553 |   (map #(* point-size %) 
    554 |        [(pt 0) (pt 1) 1 1]))
    555 | 
    556 | (defn create-apple [] 
    557 |   {:location [(rand-int width) (rand-int height)]
    558 |    :color (Color. 210 50 90)
    559 |    :type :apple}) 
    560 | 
    561 | (defn create-snake []
    562 |   {:body (list [1 1]) 
    563 |    :dir [1 0]
    564 |    :type :snake
    565 |    :color (Color. 15 160 70)})
    566 | 
    567 | (defn move [{:keys [body dir] :as snake} & grow]
    568 |   (assoc snake :body (cons (add-points (first body) dir) 
    569 | 			   (if grow body (butlast body)))))
    570 | 
    571 | (defn turn [snake newdir] 
    572 |   (if newdir (assoc snake :dir newdir) snake))
    573 | 
    574 | (defn win? [{body :body}]
    575 |   (>= (count body) win-length))
    576 | 
    577 | (defn head-overlaps-body? [{[head & body] :body}]
    578 |   (contains? (set body) head))
    579 | 
    580 | (def lose? head-overlaps-body?)
    581 | 
    582 | (defn eats? [{[snake-head] :body} {apple :location}]
    583 |    (= snake-head apple))
    584 | 
    585 | ; START: update-positions
    586 | (defn update-positions [{snake :snake, apple :apple, :as game}]
    587 |   (if (eats? snake apple)
    588 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
    589 |     (merge game {:snake (move snake)})))
    590 | ; END: update-positions
    591 | 
    592 | (defn update-direction [{snake :snake :as game} newdir]
    593 |   (merge game {:snake (turn snake newdir)}))
    594 | 
    595 | (defn reset-game [game]
    596 |   (merge game {:apple (create-apple) :snake (create-snake)}))
    597 | 
    598 | ; ----------------------------------------------------------
    599 | ; * * gui * *
    600 | ; ----------------------------------------------------------
    601 | (defn fill-point [g pt color] 
    602 |   (let [[x y width height] (point-to-screen-rect pt)]
    603 |     (.setColor g color) 
    604 |     (.fillRect g x y width height)))
    605 | 
    606 | (defmulti paint (fn [g object & _] (:type object)))
    607 | 
    608 | (defmethod paint :apple [g {:keys [location color]}] 
    609 |   (fill-point g location color))
    610 | 
    611 | (defmethod paint :snake [g {:keys [body color]}] 
    612 |   (doseq [point body]
    613 |     (fill-point g point color)))
    614 | 
    615 | (defn game-panel [frame game]
    616 |   (proxy [JPanel ActionListener KeyListener] []
    617 |     (paintComponent [g] 
    618 |       (proxy-super paintComponent g)
    619 |       (paint g (@game :snake))
    620 |       (paint g (@game :apple)))
    621 |     ; START: swap!
    622 |     (actionPerformed [e] 
    623 |       (swap! game update-positions)
    624 |       (when (lose? (@game :snake))
    625 | 	(swap! game reset-game)
    626 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    627 |     ; END: swap!
    628 |       (when (win? (@game :snake))
    629 | 	(swap! game reset-game)
    630 | 	(JOptionPane/showMessageDialog frame "You win!"))
    631 |       (.repaint this))
    632 |     (keyPressed [e] 
    633 |       (swap! game update-direction (dirs (.getKeyCode e))))
    634 |     (getPreferredSize [] 
    635 |       (Dimension. (* (inc width) point-size) 
    636 | 		  (* (inc height) point-size)))
    637 |     (keyReleased [e])
    638 |     (keyTyped [e])))
    639 | ;;;;; ! hello world
    640 | (defn game [] 
    641 |   (let [game (atom (reset-game {}))
    642 | 	frame (JFrame. "Snake")
    643 | 	panel (game-panel frame game)
    644 | 	timer (Timer. turn-millis panel)]
    645 |     (doto panel 
    646 |       (.setFocusable true)
    647 |       (.addKeyListener panel))
    648 |     (doto frame 
    649 |       (.add panel)
    650 |       (.pack)
    651 |       (.setVisible true))
    652 |     (.start timer) 
    653 | [game, timer])) 
    654 | 
    655 | ; Inspired by the snakes the have gone before:
    656 | ; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
    657 | ; ! Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 
    658 | 
    659 | (ns examples.atom-snake
    660 |   (:import (java.awt Color Dimension) 
    661 | 	   (javax.swing JPanel JFrame Timer JOptionPane)
    662 |            (java.awt.event ActionListener KeyListener))
    663 |   (:use examples.import-static))
    664 | (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
    665 | 
    666 | ; ----------------------------------------------------------
    667 | ; functional model
    668 | ; ----------------------------------------------------------
    669 | (def width 75)
    670 | (def height 50)
    671 | (def point-size 10)
    672 | (def turn-millis 75)
    673 | (def win-length 5)
    674 | (def dirs { VK_LEFT  [-1  0] 
    675 |             VK_RIGHT [ 1  0]
    676 |             VK_UP    [ 0 -1] 
    677 | 	    VK_DOWN  [ 0  1]})
    678 | 
    679 | (defn add-points [& pts] 
    680 |   (vec (apply map + pts)))
    681 | 
    682 | (defn point-to-screen-rect [pt] 
    683 |   (map #(* point-size %) 
    684 |        [(pt 0) (pt 1) 1 1]))
    685 | 
    686 | (defn create-apple [] 
    687 |   {:location [(rand-int width) (rand-int height)]
    688 |    :color (Color. 210 50 90)
    689 |    :type :apple}) 
    690 | 
    691 | (defn create-snake []
    692 |   {:body (list [1 1]) 
    693 |    :dir [1 0]
    694 |    :type :snake
    695 |    :color (Color. 15 160 70)})
    696 | 
    697 | (defn move [{:keys [body dir] :as snake} & grow]
    698 |   (assoc snake :body (cons (add-points (first body) dir) 
    699 | 			   (if grow body (butlast body)))))
    700 | 
    701 | (defn turn [snake newdir] 
    702 |   (if newdir (assoc snake :dir newdir) snake))
    703 | 
    704 | (defn win? [{body :body}]
    705 |   (>= (count body) win-length))
    706 | 
    707 | (defn head-overlaps-body? [{[head & body] :body}]
    708 |   (contains? (set body) head))
    709 | 
    710 | (def lose? head-overlaps-body?)
    711 | 
    712 | (defn eats? [{[snake-head] :body} {apple :location}]
    713 |    (= snake-head apple))
    714 | 
    715 | ; START: update-positions
    716 | (defn update-positions [{snake :snake, apple :apple, :as game}]
    717 |   (if (eats? snake apple)
    718 |     (merge game {:apple (create-apple) :snake (move snake :grow)})
    719 |     (merge game {:snake (move snake)})))
    720 | ; END: update-positions
    721 | 
    722 | (defn update-direction [{snake :snake :as game} newdir]
    723 |   (merge game {:snake (turn snake newdir)}))
    724 | 
    725 | (defn reset-game [game]
    726 |   (merge game {:apple (create-apple) :snake (create-snake)}))
    727 | 
    728 | ; ----------------------------------------------------------
    729 | ; * * gui * *
    730 | ; ----------------------------------------------------------
    731 | (defn fill-point [g pt color] 
    732 |   (let [[x y width height] (point-to-screen-rect pt)]
    733 |     (.setColor g color) 
    734 |     (.fillRect g x y width height)))
    735 | 
    736 | (defmulti paint (fn [g object & _] (:type object)))
    737 | 
    738 | (defmethod paint :apple [g {:keys [location color]}] 
    739 |   (fill-point g location color))
    740 | 
    741 | (defmethod paint :snake [g {:keys [body color]}] 
    742 |   (doseq [point body]
    743 |     (fill-point g point color)))
    744 | 
    745 | (defn game-panel [frame game]
    746 |   (proxy [JPanel ActionListener KeyListener] []
    747 |     (paintComponent [g] 
    748 |       (proxy-super paintComponent g)
    749 |       (paint g (@game :snake))
    750 |       (paint g (@game :apple)))
    751 |     ; START: swap!
    752 |     (actionPerformed [e] 
    753 |       (swap! game update-positions)
    754 |       (when (lose? (@game :snake))
    755 | 	(swap! game reset-game)
    756 | 	(JOptionPane/showMessageDialog frame "You lose!"))
    757 |     ; END: swap!
    758 |       (when (win? (@game :snake))
    759 | 	(swap! game reset-game)
    760 | 	(JOptionPane/showMessageDialog frame "You win!"))
    761 |       (.repaint this))
    762 |     (keyPressed [e] 
    763 |       (swap! game update-direction (dirs (.getKeyCode e))))
    764 |     (getPreferredSize [] 
    765 |       (Dimension. (* (inc width) point-size) 
    766 | 		  (* (inc height) point-size)))
    767 |     (keyReleased [e])
    768 |     (keyTyped [e])))
    769 | ;;;;; ! hello world
    770 | (defn game [] 
    771 |   (let [game (atom (reset-game {}))
    772 | 	frame (JFrame. "Snake")
    773 | 	panel (game-panel frame game)
    774 | 	timer (Timer. turn-millis panel)]
    775 |     (doto panel 
    776 |       (.setFocusable true)
    777 |       (.addKeyListener panel))
    778 |     (doto frame 
    779 |       (.add panel)
    780 |       (.pack)
    781 |       (.setVisible true))
    782 |     (.start timer) 
    783 | [game, timer])) 
    784 | 
    785 | 
    
    
    --------------------------------------------------------------------------------