Giter Club home page Giter Club logo

civet's Introduction

Civet

Build Status NPM Version NPM Downloads Coverage Status Discord Open Collective

The modern way to write TypeScript.

Quickstart Guide

# Install
npm install -g @danielx/civet
# Run Civet code directly in a REPL
civet
# Transpile typed Civet code into TypeScript in a REPL
civet -c
# Compile Civet source file to TypeScript
civet < source.civet > output.ts
# Execute a .civet script
civet source.civet ...args...
# Execute a .civet source file in node
node --import @danielx/civet/register source.civet

image

Code Sample

ts, {CompilerOptions} from typescript

DefaultCompilerOptions : CompilerOptions :=
  allowNonTsExtensions: true
  allowJs: true
  target: ts.ScriptTarget.Latest
  moduleResolution: ts.ModuleResolutionKind.NodeJs
  module: ts.ModuleKind.CommonJS
  allowSyntheticDefaultImports: true
  experimentalDecorators: true

fileCache : Record<string, any> := {}

createCompilerHost := (options: CompilerOptions, moduleSearchLocations : string[]) ->
  fileExists := (fileName: string) : boolean ->
    fileCache[fileName]?

  readFile := (fileName: string) ->
    fileCache[fileName]

Overview

Civet is essentially a tasteful superset of TypeScript.

Implementations of New and Proposed ES Features

See the documentation for examples of these and other features.

  • Pattern matching (based on TC39 proposal)
    • switch can match patterns like [{type: "text", name}, ...rest]
  • Pipe operator (based on F# pipes, Hack pipes and the TC39 proposal)
    • data |> Object.keys |> console.log equivalent to console.log(Object.keys(data))
    • Use single-argument arrow functions or & shorthand to specify how to use left-hand side
    • |> await, |> yield, and |> return (at end) for wrapping left-hand side with that operation
  • Short function block syntax like Ruby symbol to proc, Crystal, Elm record access
    • Access: x.map &.name or x.map .namex.map(a => a.name)
    • Nested access + slices: x.map &.profile?.name[0...3]x.map(a => a.profile?.name.slice(0, 3))
    • Function call: x.map &.callback a, bx.map($ => $.callback(a, b))
    • Unary operators: x.map !!&x.map($ => !!$)
    • Binary operators: x.map &+1x.map($ => $+1)
  • Object literal shorthand
    • {foo()}{foo: foo()}, {props.foo}{foo: props.foo}
    • {`${x}${y}`: z}{[`${x}${y}`]: z}
    • data.{x,y} or data{x,y}{x: data.x, y: data.y}
    • Flagging shorthand based on from LiveScript: {+debug, -live, !verbose}{debug: true, live: false, verbose: false}
  • Custom infix operators from any two-argument function
  • do expressions, if expressions, for expressions

Convenience for ES6+ Features

  • Const assignment shorthand: a := bconst a = b, {a, b} := cconst {a, b} = c
  • Let assignment shorthand: a .= blet a = b
  • Typed versions of above: a: number .= 5let a: number = 5 (but note that a: number = 5 is the object literal {a: (number = 5)}).
  • @#idthis.#id shorthand for private identifiers
  • import shorthand: x from ./ximport x from "./x"
  • Dynamic import shorthand: import './x' not at top level (e.g. await import './x' or inside a function) → import('./x')
  • Optional import rename syntax that corresponds to destructuring rename import {x: y} from "./z"import {x as y} from "./z". You can still use as to be compatible with existing ES imports.
  • export shorthand: export x, yexport {x, y}
  • Triple backtick Template Strings remove leading indentation for clarity
  • Class constructor shorthand @( ... )
  • ClassStaticBlock @ { ... }
  • < as extends shorthand
  • /// Block RegExp like Python re.X

JSX Enhancements

Inspired by solid-dsl discussions and jsx spec issues

  • Indentation: instead of explicitly closing <tag>s or <>s, you can indent the children and Civet will close your tags for you
  • Multiple adjacent elements and/or fragments get automatically combined into a fragment.
  • Arrow function children do not need to be wrapped in braces (assuming they are not preceded by text); this is unambiguous because > isn't valid JSX text. For example, <For> (item) => ... (where function body can be indented).
  • #foo shorthand for id="foo"; also #"foo bar", #`foo ${bar}`, #{expr}
  • .foo shorthand for class="foo" (but must be at least one space after tag name); also .foo.bar, ."foo bar", .`foo ${bar}`, .{expr}
    • "civet react" flag uses className instead of class
  • +foo shorthand for foo={true}, -foo/!foo shorthand for foo={false}
  • Any braced object literal can be used as an attribute: {foo}foo={foo}, {foo: bar}foo={bar}, {...foo} remains as is; methods and getters/setters work too.
  • Attribute ...foo shorthand for {...foo}
  • Attribute values without whitespace or suitably wrapped (parenthesized expressions, strings and template strings, regular expressions, array literals, braced object literals) do not need braces: foo=barfoo={bar}, count=count()count={count()}, sum=x+1sum={x+1}, list=[1, 2, 3]list={[1, 2, 3]}
  • Attributes can use computed property names: [expr]={value}{...{[expr]: value}}
  • "civet solid" flag adds correct types for JSX elements and fragments. Use "civet solid client" (default) for client-only code, "civet solid server" for server-only code (SSR only), or "civet solid client server" for isomorphic code that runs on client and server (SSR + hydration).
  • XML comments: <!-- ... -->{/* ... */}

TypeScript Enhancements

Changes from ES6

  • Implicit returns, even for multi-statement functions (avoid by specifying a void return type, adding a trailing ; or explicit return, or via the directive "civet -implicitReturns")
  • Disallow no parens on single argument arrow function. x => ... must become (x) => ... The reasoning is x -> ... => x(function() ...) in CoffeeScript and having -> and => behave more differently than they already do is bad. Passing an anonymous function to an application without parens is also convenient.
  • for(i of x) ... defaults to const declaration → for(const i of x) ...
  • Disallow comma operator in conditionals and many other places. if x, y is not allowed. But for i = 0, l = a.length; i < l; i++, i *= 2 is allowed.
  • Comma operator in case/when instead becomes multiple conditions.
  • Numbers can't end with a dot (otherwise would be ambiguous with CoffeeScript slices y[0..x]). This also implies that you can't access properties of numbers with 1..toString() use 1.toString() instead. When exponent follows a dot it is treated as a property access since an exponent could be a valid property 1.e101..e10. The workaround is to add a trailing zero 1.0e10 or remove the dot before the exponent 1e10.
  • Additional reserved words and, or, loop, until, unless
  • Experimental decorator syntax is @@ instead of @ because @ is premium real estate and @idthis.id, and @ is also static fields/methods, etc.
    @@classDecorator
    class X
      @@methodDecorator
      method() {}
    
  • when inside switch automatically breaks and adds block scope.
  • else inside switch adds block scope.
  • No whitespace between unary operators and operands. Mandatory whitespace between condition and ternary ? ex. x ? a : b since x? is the unary existential operator.
  • Labels written :label (except for special case $: for Svelte)

Scripting Improvements

  • Shebang line is kept unmodified in output
    #!./node_modules/.bin/ts-node
    console.log "hi"
    

Comparison to CoffeeScript

Take a look at this detailed Civet // CoffeeScript comparision

ECMAScript Compatibility

You can specify "civet" prologue directives to increase compatibility with ECMAScript/TypeScript:

Configuration What it enables
-implicit-returns turn off implicit return of last value in functions

Put them at the top of your file:

"civet -implicit-returns"

Your can separate multiple options with spaces.

Deno Compatibility

TypeScript only allows importing .ts files as .js. Deno follows ESM and requires importing files with the correct extension.

Civet automatically rewrites imports to work around this issue in TS.

When Civet detects it is running in Deno rewriting imports is turned off. If for some reason Civet fails to detect running in Deno you can turn off rewriting imports manually with these configuration options:

Configuration What it enables
-rewrite-ts-imports disable rewriting .ts -> .js in imports
deno currently just disables rewriting imports but could add more deno specific options in the future

Other Options

The "civet" prologue directive can also specify the following options:

Configuration What it enables
rewrite-civet-imports=.ext Rewrite import "file.civet" to import "file.ext"
tab=NNN treat tab like NNN spaces (default=1)

For example, "civet tab=2" or "civet tab=4" lets you mix tabs and spaces in a file and be treated like they'd render in VSCode with editor.tabSize set accordingly.

Using Civet in your Node.js Environment

You have now been convinced that Civet is right for your current/next project. Here is how to set up your environment to get productive right away and have a Good Time℠.

Testing

Code coverage with c8 "just works" thanks to their source map integration and Civet's source maps.

Currently Civet's ESM loader depends on ts-node

c8 + Mocha

package.json

  "scripts": {
    "test": "c8 mocha",
    ...
  },
  "c8": {
    "extension": [
      ".civet"
    ]
  },
  "mocha": {
    "extension": [
      "civet"
    ],
    "loader": [
      "ts-node/esm",
      "@danielx/civet/esm"
    ],
    ...
  ...

ts-node must be configured with transpileOnly (it can't resolve alternative extensions). Also I think module needs to be at least ES2020 for the Civet ESM loader to work.

tsconfig.json

  ...
  "ts-node": {
    "transpileOnly": true,
    "compilerOptions": {
      "module": "ES2020"
    }
  }

If you don't care for code coverage you can skip c8 (but it is so easy why not keep it?).

You can also add .js and .ts extensions if you want to mix and match! Even .coffee will work if you require coffeescript/register or add a loader for it.

Execute the tests

yarn test

Step 4: Enjoy!

Developing

Use the alpha version of Civet Language Server

The language server provides syntax highlighting, completions, hover documentation, symbols outline, red squigglies, and go to definition.


Q? Why can't I just use the built-in VSCode TypeScript LSP?

A: VSCode's built in TypeScript LSP can't resolve non .ts/.js, not even with plugins. Maybe one day they'll allow for plugins that let you adjust the resolver and insert a transpilation step but until then a separate language server is necessary.


Q? Sometimes the file outline disappears and the red squigglies are all in the wrong place and maybe a notification pops up about some kind of LSP error.

A: I'm sorry that happened to you but the Civet Language Server is still alpha and improving rapidly. Please let me know exactly what happened and I'll try to do better next time.

It may happen when there is a syntax error in your Civet file. You can check and see if it compiles using the CLI tool in the meantime.

Please do submit bug reports / feature requests.

Building

I strongly recommend using esbuild for building / packaging your Civet project.

import esbuild from 'esbuild'
import civetPlugin from '@danielx/civet/esbuild-plugin'

esbuild.build({
  ...,
  plugins: [
    civetPlugin
  ]
}).catch(() => process.exit(1))

It's super fast and works great!

Code Coverage

Coverage Status

Sponsorship

If you are so inclined, you can sponsor Civet on Open Collective.

civet's People

Contributors

aleksrutins avatar argeento avatar bbrk24 avatar bkuri avatar dependabot[bot] avatar edemaine avatar esthedebeste avatar iamipanda avatar iliocatallo avatar irian3x3 avatar krist7599555 avatar lorefnon avatar milo123459 avatar mokshit06 avatar p2edwards avatar samualtnorman avatar shaunchander avatar strd6 avatar tombyrer avatar zolomatok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

civet's Issues

Cannot find module 'node:readline'

Given a simple program:

var something = "else"

when compiling, I get the following error:

Error: Cannot find module 'node:readline'
Require stack:
- /Users/zolo/project/ftl/node_modules/@danielx/civet/dist/civet
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15)
    at Function.Module._load (internal/modules/cjs/loader.js:746:27)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at Object.<anonymous> (/Users/zolo/project/ftl/node_modules/@danielx/civet/dist/civet:13:12)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/Users/zolo/project/ftl/node_modules/@danielx/civet/dist/civet' ]
}

Civet is installed locally (not globally) and is ran like so:

civet ./something.civet ./something.ts

`try` expression

y = try x()
y = (function() { try { return x() } catch {}})()

Bug with properties and methods in object literals

  • {func(){5}, x} compiles correctly as {func(){return 5}, x}
  • {func(){}, x} compiles incorrectly as {func() {return {}, x }}
  • {x, func(){5}} fails to compile at all; expected {x, func(){return 5}} which works in ES

LSP Semantic Token Provider + syntax highlighting

Trying to merge the TypeScript TextMate grammar and an updated CoffeeScript grammar seems like it would be an unspeakable nightmare. So it would be nice if the Civet parser / LSP could provide semantic tokens.

Essentially tokens that have {$loc} would be source tokens styled from the nearest ancestor that has some sort of {sematicToken} property. It might require passing a flag into the parser and skipping normal AST processing instead constructing a stream of semantic tokens.

https://code.visualstudio.com/api/references/vscode-api#DocumentSemanticTokensProvider (looks like it is very similar to how source maps work)

Use refs for non-simple chained comparisons

Use a ref in non-simple chained comparisons so not to repeat evaluations of complex expressions.

f1(x) < f2(x) < f3(x) < f4(x)
let ref
f1(x) < (ref = f2(x)) && ref < (ref = f3(x)) && ref < f4(x)

LSP dependency on tsconfig.json

The LSP should re-parse everything when tsconfig.json changes. Currently I need to restart. By contrast, .tsx files update when I change tsconfig.json.

Ideally the same for node_modules/@danielx/civet, for upgrading Civet more seamlessly.

Support import named type

import A, {B, type C} from 'library'

Should compile to:

import A, {B, type C} from 'library'

and should compile to:

import A, {B} from 'library'

when passed compiler option js: true.

civet crashes

On Windows:

$ npm install -g @danielx/civet

added 1 package, and audited 2 packages in 3s

found 0 vulnerabilities

$ civet
node:fs:221
  let mode = stats[1];
                  ^

TypeError: Cannot read properties of undefined (reading '1')
    at isFileType (node:fs:221:19)
    at Object.readFileSync (node:fs:470:16)
    at Object.<anonymous> (C:\Users\edemaine\AppData\Roaming\npm\node_modules\@danielx\civet\dist\civet:12:12)
    at Module._compile (node:internal/modules/cjs/loader:1120:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1174:10)
    at Module.load (node:internal/modules/cjs/loader:998:32)
    at Module._load (node:internal/modules/cjs/loader:839:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

Node.js v18.7.0

On Linux:

$ npm install -g @danielx/civet

added 1 package, and audited 2 packages in 2s

found 0 vulnerabilities

$ civet
node:internal/fs/utils:348
    throw err;
    ^

Error: EAGAIN: resource temporarily unavailable, read
    at Object.readSync (node:fs:748:3)
    at tryReadSync (node:fs:448:20)
    at Object.readFileSync (node:fs:494:19)
    at Object.<anonymous> (/afs/csail.mit.edu/u/e/edemaine/.nvm/versions/node/v18.12.1/lib/node_modules/@danielx/civet/dist/civet:12:12)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47 {
  errno: -11,
  syscall: 'read',
  code: 'EAGAIN'
}

Node.js v18.12.1

`x = y == z` fails to parse

x = y == z fails to parse (even with coffeeCompat turned on).

x = (y == z) works, but the parentheses shouldn't be necessary.

Anonymous class bug

Parses:

class
  static a = 5

Doesn't parse:

class
  @a = 5
class
  constructor(x);

These examples work if the class has a name, as in class X.

CLI for type checking

Vue has a cli utility called vue-tsc, which can be used for type-checking in CI env etc. It would be good to have something similar for civet.

`do` expressions without `while`/`until`

Oren wanted something like this on Discord: (originally suggested without do keyword)

x = do
  y := 5
  y + 1

It does seem like a natural extension to do ... while currently supported, i.e., make the while optional. It also corresponds to this proposal (though we don't need that proposal for if/while expressions).

Related, I expected this to work, but it doesn't parse: (it works without the :=)

x = if true
  y := 5
  y + 1

Current workarounds, which work but are a bit inefficient as they create arrays:

x = (do
  y := 5
  y + 1
while false)[0]
x = (for nothing of [1]
  y := 5
  y + 1
)[0]

Decorator syntax

What is the Civet syntax for TS decorators?

Given:

const printable = (target: any, memberName: string) =>
    console.log memberName

class Person
    @printable
    name = 'Jon'

I get the warning Member 'printMemberName' implicitly has an 'any' type.typescript(7008) because @ is used for static members.

The Code Sample at the top of the readme contains the

experimentalDecorators: true

flag in the compiler options, so I assume they are supported.

Cannot declare multiline function

I'm not sure what I'm missing from the readme, but I cannot seem to able to define multiline functions.

Given:

let bla = (one, two) ->
    console.log one
    console.log two

It compiles to:

let bla = function(one, two) {}
    console.log(one)
    console.log(two)

There is just a single line break after -> and the indentation is the same on the 2nd and 3rd line.
I'm using the VSC extensions, so the language server is already displaying me an error before compilation, saying
Cannot find name 'one'

Changing it to an arrow function:

let bla = (one, two) =>
    console.log one
    console.log two

Results in:

let bla = (one, two) =>
    console.log(one)
    console.log(two)

Here's the same with types:

let bla = (one: string, two: string) =>
    console.log one
    console.log two

It turns to:

let bla = (one: string, two: string) =>
    console.log(one)
    console.log(two)

Operator overloading and custom operators

This is a feature request rather than an issue. Originally I wanted to inquire about macro support, but to be honest what I'm mostly looking for is operator overloading and custom operators.

Swift has a wonderful feature where you can overload prefix, infix and postfix operators or create new ones to your heart's content and it's the best. It has the potential to make the code very succinct so such feature I believe is such an obvious fit in Civet and CoffeeScript.

For example, for a long time, Swift had an awfully verbose regex matching API, but I could just define the ~= operator on String and I could express with two characters that otherwise would have taken 4 lines to do:

url ~= ".*/account/.*/login"

Or how about using operators for matrices? Makes so much sense.
Or how about some reactive code where you could just add together multiple observables to get a combined signal?
Or how about a language that uses := as a readonly shorthand? 😉

Mathematical operators is one of the best known and most succinct language we have as humans and I feel like it's such a waste to use them only for numbers.

I would really really love a Swift-like solution. 🚀

Implicit object literal calls vs class definitions

@IamIpanda posted this example:

class A
  a: number
  
class B extends A
  b: number

https://civet-web.vercel.app/#Y2xhc3MgQQogIGE6IG51bWJlcgogIApjbGFzcyBCIGV4dGVuZHMgQQogIGI6IG51bWJlcg==

Currently, this looks like and gets treated as a function call A({b: number}). But this is almost certainly not what the user meant. This is a special feature of classes where we can have bare type declarations like b: number (FieldDefinition within ClassElementDefinition within ClassElement).

@STRd6 What's the best way to communicate that ExtendsClause shouldn't, at the top level, use the implicit-function-call-with-implicit-object rule? Do we need to duplicate the whole rule tree from LeftHandSideExpression? Or set some state and disable caching? Pretty annoying...

It's not strictly necessary, but we ideally would still allow implicit function calls not at the top level, e.g.:

class B extends (A
  b: c
)

Update: Oh, I notice IndentedApplicationAllowed is exactly for this purpose. Just need to figure out which cache rules to invalidate...

Another object literal indentation issue

json: (srcFileName, outFileName) ->
  version: 3
updateSourceMap: (outputStr, inputPos) -> outputStr

currently transpiles to

{json: function(srcFileName, outFileName) {
  return {version: 3,
updateSourceMap: function(outputStr, inputPos) { return outputStr }}
}}

(based on real code from source/util.coffee)

Playground

VSC extension recheck

I'm really really happy about the existence of a real time checker in the form of the VS Code plugin.
I know it's in an early state.

Often times it gets stuck, showing errors for lines that don't exist anymore.
I noticed that its memory is bound to file names, so if I delete test.civet, but rename another file to test.civet, it will instantly start to show the phantom errors in that file.

Is there a way to ask the extension to forget everything and recheck the whole file from scratch? Maybe a keyboard shortcut? Is that possible?

PS: off topic, but love that we have a file icon now

Imports

More of a question than an issue.
What's the import situation going to be (is?) with Civet?
Will we be able to import other .civet files as ES6 modules like in typescript?
Or is it going to be like in Coffee where you can import other .coffee files as Node modules, but not as ES6 modules?

Indentation based jsx syntax

Hello, thanks a lot for creating Civet - This looks great. I spent some time playing around with it yesterday and loved the DX.

Since this language is quite early, I was wondering if we could eliminate the need for closing xml tags in jsx, and adopt an indentation based syntax similar to imba. I think this will make it more coherent with the rest of the language as well.

Imba is tied to its own dom/rendering impl. where as I am trying to use civet with solidjs which has its own jsx preprocessor.

Keywords trip up the parser in unexpected ways

There are many cases where keywords cause the parsing to fail:

S.obj nullable: false

fails to parse because "able" is unexpected after null

Also tangentially, quoting nullable doesn't help

Similarly S.obj true: false fails to parse.

If I update the NullLiteral to be defined as :

NullLiteral
  /\b(null)\b/ ->
    return { $loc, token: $1 }

The nullable issue goes away, but things break if I add wordbreak boundaries to other literals like true/false. Perhaps there is a better/recommended way to handle this in hera ?

TypeScript `declare`

  • declare let $: JQuery doesn't parse
  • declare global {}, declare module "hot-new-module"; and declare module "path" {} parse as function calls
  • declare class

Improve indentation tracking

So I frequently run into function application getting introduced at unexpected places. An example that currently behaves weirdly is:

 foo := [
  a: 10
  b: 20
]
const foo = [
  {a: 10({
  b: 20,
})}
]

There were a few previously discussed comments which were also variations of same problem: #84 (comment), #83 (comment)

I think crux of the problem is that our rules are evaluated inside out and indentation tracking (PushIndent, PopIndent) is interspersed in the same pass.

So when a: 10 is getting parsed, the rule for NestedElementList:

NestedElementList
  PushIndent NestedElement*:elements PopIndent ->

is yet to be evaluated. The PushIndent in this rule hasn't yet run so module.currentIndent is still same as previous level (in this case 0)

Because of that the next indented expression b: 20 appears to be indented further than current level, which is wrong.

This can probably be addressed by a smart rearrangement of rules on a case by case basis, but that is pretty hard to reason about (atleast for me).

A simpler solution is to potentially separate out indentation tracking in a separate previous pass (perhaps alongside lexing).

Alternatively the parser could apply rules left -> right and also provide a way to revert any shared state modifications if rules to right don't match.

Better notation to re-use `&`?

Currently, if you want a quick function that uses the input twice, e.g. ($) => $+$, you can write &+$. I find this syntactically inappropriate, as $ doesn't appear until you use it the second time; I don't find it at all intuitive that & binds $. And it's especially bad if you use $ yourself or nest & functions inside of each other.

I would rather find a notation that is similar in the first and second uses, or that doesn't use an existing identifier. Obviously &+& has a meaning though, namely, ($) => $ + ($$) => $$. And it could get more ambiguous with e.g. &(&).

Some possible proposals (which I think I mentioned previously in Discord):

  1. A second & refers to the same variable as the first, so &+& would mean ($) => $+$. This would forbid nesting a & function shorthand inside another one, but we can use alternatives to just &. For example, if you want the current meaning of &+&, you could use something like &+&& or &+&2 or &1+&2.
  2. Almost the opposite: && or &1 could mean "re-use the last & variable", similar to how $ behaves now. We could extend to &&& or &2 referring to two levels up, etc.
  3. Similar to 2, we could use a special notation for the current & argument, like ^, or ^^ for two levels up etc. Or &^, &^^, etc.

It occurs to me that && is probably syntactically ambiguous with the and operator, so the above suggestions are only really serious with the &n notation for an integer n, or something else like &^.

FWIW, Ruby, Crystal, and Elm don't seem to have anything for this. So another option (other than leaving things as is) would be:

  1. Forbid accessing the argument a second time. &+$ would become ($$) => $$ + $. If you want ($) => $+$, write that.

Also possibly related to pipe operator and #75 (comment) ...

Arrow function can be non intuitive for JS developers

Currently y := x => x + 1 compiles to const y = x(() => x + 1) which may be a source of confusion for people coming from js which allows parenthesis to be omitted for single argument.

Should we consider adopting the js behavior by default (unless coffeeCompat is set to true) ?

Perhaps to reduce ambiguity we enforce that arg list can never be omitted, so instead of y := -> 1 we'd have to write y := () -> 1

Indentations in the middle

CoffeeScript allows for indentation in the middle of e.g. an array literal:

[1
 2
 3]

Civet won't parse this, even after #26.

More generally, I think whenever we have indented forms, we often need to support switching from regular form to indented form, at least once. For example:

[1, 2, 3   # regular form
 4, 5, 6   # switch to indented
 7, 8, 9]

Oddly CoffeeScript (see ArgElisionList in grammar.coffee) supports additional indentation blocks in the middle of an array literal:

[1
 2
   3
]

This is probably less important to support. But one indentation level is pretty common I think.

Remove non-civet file activationEvents ?

Do we need the non-civet-file activation events here ?

I'd like civet extension to not take over for other non-civet related pure-ts/js projects. I can disable it per workspace but I juggle between a dozen plus repos quite frequently so its not ideal.

Playground is not working in Safari

Safari 16.0
macOS 12.6

It works in Chrome on the same computer.

There's an error in the console, it may be related.
SyntaxError: Invalid regular expression: invalid group specifier name

playground

Fix &block precedence

When &blocks are combined with unary ops, binary ops, and call expressions the parse tree doesn't reflect the actual structure. It works by chance in simple cases but won't in complex ones.

TypeScript `namespace` and `export interface`

Here's a small example from TypeScript:

namespace Animals {
  export interface Legged {
    numberOfLegs: number;
  }
  export class Dog {}
}
  • namespace doesn't have an indentation form; it needs braces
  • export interface doesn't parse in general
  • export class Dog doesn't parse inside namespace Animals {...}

Top-level string doesn't interpolate

"civet coffeeInterpolation"
"Hello #{name}!"

compiles to "Hello #{name}!" (incorrect)

"civet coffeeInterpolation"
console.log "Hello #{name}!"

compiles to console.log(`Hello ${name}`) (correct)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.