├── 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 |
We have these animals: 9 |
.?)(?${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 |
36 | {% endfor %}
37 |
38 |
39 |
40 |
41 |
42 | {% block content %}
43 | {% endblock %}
44 |
45 |
46 |
47 |
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 | 
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 | 
156 |
157 | **"better-comments.strict": false**
158 |
159 | 
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 |
--------------------------------------------------------------------------------