Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Tact language syntax for prism.js #288

Closed
SwiftAdviser opened this issue Aug 4, 2023 · 21 comments
Closed

Update Tact language syntax for prism.js #288

SwiftAdviser opened this issue Aug 4, 2023 · 21 comments
Assignees
Labels
Approved This proposal is approved by the committee footstep This is a TON Footstep issue

Comments

@SwiftAdviser
Copy link
Contributor

SwiftAdviser commented Aug 4, 2023

Summary

Update syntax highlighting support for the Tact language in prism.js

Context

Prism.js is a popular syntax highlighting library used for code snippets. However, support for the Tact language has outdated syntax. The Tact language is gaining traction and being used in various projects, including the ton.org/dev and docs.ton.org website. Therefore, it is necessary to add syntax highlighting support for the Tact language in prism.js.

Currently, we already have syntax highlight for prism.js, but it's outdated:
https://github.com/ton-community/ton-docs/blob/278493f6e1debad7f370924c6d1241ad14a2eba2/src/theme/prism/prism-tact.js#L4

Useful links:

Goals

  • Add Tact language highlighter to prism.js

Deliverables

  • Pull Request (PR) to add Tact language support to prism.js

Definition of Done

  • PR is sent to the prism.js repository

Reward

  • Footstep SBT
  • $300 in TON equivalent

Total: $300

Oriental Release Date

30.08.23

@SwiftAdviser SwiftAdviser added the footstep This is a TON Footstep issue label Aug 4, 2023
@SwiftAdviser
Copy link
Contributor Author

SwiftAdviser commented Aug 4, 2023

oh, it's already completed, found in the history of TON Docs:
https://github.com/ton-community/ton-docs/blob/278493f6e1debad7f370924c6d1241ad14a2eba2/src/theme/prism/prism-tact.js#L4

UPD: it's outdated, we need new version

@SwiftAdviser SwiftAdviser changed the title Translate Tact language syntax for prism.js Update Tact language syntax for prism.js Aug 4, 2023
@SwiftAdviser SwiftAdviser reopened this Aug 4, 2023
@Gusarich Gusarich added the Approved This proposal is approved by the committee label Aug 4, 2023
@novusnota
Copy link
Contributor

Ready to work on it — please, assign me to this footstep.

P.S.: I was recently researching some lexers/parsers for a couple of languages, Tact looks quite familiar in that regard :)

@Gusarich
Copy link
Collaborator

Gusarich commented Aug 7, 2023

@novusnota hi! Just assigned you. Good luck and don’t forget to share your progress here!

@novusnota
Copy link
Contributor

@Gusarich great! sure thing :)

@SwiftAdviser
Copy link
Contributor Author

SwiftAdviser commented Aug 7, 2023

@novusnota here is a useful link for you to build a parser:

https://github.com/tact-lang/tact/blob/main/src/grammar/grammar.ohm

@jojudre
Copy link

jojudre commented Aug 9, 2023

wow waiting this improvement

@novusnota
Copy link
Contributor

@Gusarich @SwiftAdviser @jojudre

Almost there, here's a small snippet (doesn't actually exist on the website, it's just me testing locally),
check out before (on the left) and after on the right:

Color theme on ton-docs doesn't color much (see get, fun, let, const, return and other keywords being italic), so I'll be testing highlighting locally on the prism website and writing prism-internal tests tomorrow.

@howardpen9
Copy link

Awesome!

@Gusarich @SwiftAdviser @jojudre

Almost there, here's a small snippet (doesn't actually exist on the website, it's just me testing locally), check out before (on the left) and after on the right:


Color theme on ton-docs doesn't color much (see get, fun, let, const, return and other keywords being italic), so I'll be testing highlighting locally on the prism website and writing prism-internal tests tomorrow.

Awesome!

@novusnota
Copy link
Contributor

@SwiftAdviser @delovoyhomie

Done and lookin' good:

PR to ton-docs: feat: Updated Tact language highlighting for Prism.js
PR to Prism.js (with examples and tests!): Add Tact language

The second one would be reviewed and merged as they release Prism.js v2 ✌

TON wallet address for reward: EQDew1rvHuMmMkmxG_fQahGymzIOF2_9TpgLftMUuxpKLE_u
Name for the Hall of Fame: Novus Nota

@delovoyhomie
Copy link
Collaborator

@reveloper @SwiftAdviser, please make a review for documentation so that we can fully complete this footstep.

@reveloper
Copy link
Contributor

@delovoyhomie Ok, will do it ASAP.

@delovoyhomie
Copy link
Collaborator

@SwiftAdviser @reveloper, Is it looks good to you?

@reveloper
Copy link
Contributor

@delovoyhomie
Sorry, we had no time to review it, but it will be evaluated in the next few days.

@novusnota
Copy link
Contributor

@reveloper here's the sample I used on the screenshot above:

import "@stdlib/ownable";

struct SampleStruct {
    message: Int as uint32;
}

message(0x1234) MyMessage {
    data: SampleStruct;
}

trait MyTrait {
    get fun traitState(): Int {
        return 0;
    }
}

contract MyContract with MyTrait, Ownable? {
    owner: Address; // comment
    value: Int as uint32 = 1230;
    counters: map<Int, Address> /* Address, "Address" */ ;
    map: map<Int as int16, Int as uint32>;
    b: bounced<MyMessage>;

    init(owner: Address) {
        self.owner = owner;
    }
    bounced() {}
    virtual fun overrideMe() {}
}

mutates get fun noName() { /* Multi-line comment */ }

@reveloper
Copy link
Contributor

reveloper commented Aug 28, 2023

@novusnota, @delovoyhomie I checked locally with several Tact contracts, have no find issues according to footstep task, LGTM.

One note to think about, for me Italic style for keywords(get, fun, let,...) is not clear enough (same for FunC). I would prefer to highlight them with alternative colors (as it is implemented for Nextra Tact docs).

Perhaps this should be reconsider as separate update or declined (depending on dev community thoughts).

@novusnota
Copy link
Contributor

novusnota commented Aug 28, 2023

@reveloper Fully agree, but this italic style is due to the particular prism.js theme currently in the use on ton-docs, and as such — it has nothing to do with the grammars of Tact (prism-tact.js), nor with grammars of FunC (prism-func.js). They only declare some symbols to semantically mean keywords, but how to showcase all keywords (bold, italic or with a particular color) is a job of the current highlighting theme.

tl;dr: one should find a common theme used on ton-docs and make an adjustment there directly, so that's a separate update, unrelated to this footstep :)

@novusnota
Copy link
Contributor

@reveloper Something like that?

Idk where does current colors for the highlighting come from, though. Dracula color theme blocks show up unused in the docusaurus.config.js#L5.

So I've colored keywords locally in a hacky way — using JS after everything is loaded, here's the snippet:

var token = document.querySelectorAll('span.token.keyword');
token.forEach(t => t.style = "color: rgb(245, 110, 108);");

@howardpen9
Copy link

how about the code here we prepared for NFT standard:

`const minTonsForStorage: Int = ton("0.03");
const gasConsumption: Int = ton("0.03");

contract NftCollection {

    next_item_index: Int as uint32 = 0;
    owner_address: Address;
    royalty_params: RoyaltyParams;                      
    collection_content: Cell;   

    init(owner_address: Address, collection_content: Cell, royalty_params: RoyaltyParams){
        self.owner_address = owner_address;
        self.collection_content = collection_content; 
        self.royalty_params = royalty_params;
    }

    receive("Mint"){
        let ctx: Context = context(); // get sender Info

        let msgValue: Int = ctx.value;
        let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
        let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
        msgValue = msgValue - (storageFee + gasConsumption);

        self.mint(ctx.sender, msgValue);
    }

    // ===== Private Methods ===== //
    fun mint(sender: Address, msgValue: Int) {
        require(self.next_item_index >= 0, "non-sequential NFTs");
        
        let nft_init: StateInit = self.getNftItemInit(self.next_item_index);
        send(SendParameters{
                to: contractAddress(nft_init), 
                value: msgValue, 
                bounce: false,
                mode: SendIgnoreErrors,
                body: Transfer {
                    query_id: 0,
                    new_owner: sender,
                    response_destination: self.owner_address,
                    custom_payload: emptyCell(),
                    forward_amount: 0,
                    forward_payload: emptySlice()
                }.toCell(),
                code: nft_init.code,
                data: nft_init.data
            });
        self.next_item_index = self.next_item_index + 1;
    }

    receive(msg: GetRoyaltyParams) {   
        let ctx: Context = context(); // get sender Info
        send(SendParameters{
            to: ctx.sender,
            value: 0,
            mode: 64, 
            bounce: false,
            body: ReportRoyaltyParams {
                query_id: msg.query_id,
                numerator:  self.royalty_params.numerator,
                denominator: self.royalty_params.denominator,
                destination: self.owner_address
            }.toCell()
        });        
    }

    // ------------------ Get Function  ------------------ //
    get fun get_collection_data(): CollectionData {     
        let b: StringBuilder = beginString();
        let collectionDataString: String = self.collection_content.asSlice().asString();
        b.append(collectionDataString);
        b.append("meta.json"); // You can changed this your self.
        return CollectionData{
            next_item_index: self.next_item_index, 
            collection_content: b.toCell(), 
            owner_address: self.owner_address
        };
    }

    get fun get_nft_address_by_index(item_index: Int): Address?{      
        let initCode: StateInit = self.getNftItemInit(item_index);
        return contractAddress(initCode);
    }

    get fun getNftItemInit(item_index: Int): StateInit {
        return initOf NftItem(myAddress(), item_index, self.owner_address, self.collection_content);
    }
    
    get fun get_nft_content(index: Int, individual_content: Cell): Cell { 
        let b: StringBuilder = beginString();
        let ic: String = individual_content.asSlice().asString();
        b.append(ic);
        return b.toCell();
    }

    get fun royalty_params(): RoyaltyParams {
        return self.royalty_params;
    }
}

message(0x693d3950) GetRoyaltyParams {
    query_id: Int as uint64;
}

message(0xa8cb00ad) ReportRoyaltyParams {
    query_id: Int as uint64;
    numerator:  Int as uint16;
    denominator: Int as uint16;
    destination: Address;
}

struct CollectionData {
    next_item_index: Int;
    collection_content: Cell;
    owner_address: Address;
}

struct RoyaltyParams {
    numerator: Int;
    denominator: Int;
    destination: Address;
}

// ---------------------------------------------------------------------------------------------------- //
// ---------------------------------------------------------------------------------------------------- //

contract NftItem {
    collection_address: Address;
    item_index: Int; 
    owner: Address;
    individual_content: Cell;
    is_initialized: Bool;

    init(collection_address: Address, item_index: Int, owner: Address, individual_content: Cell){
        self.collection_address = collection_address;
        self.item_index = item_index;
        self.owner = collection_address;
        self.individual_content = individual_content;
        self.is_initialized = false;
    }

    receive(msg: Transfer){
        let ctx: Context = context();
    
        let msgValue: Int = ctx.value; // Check the gasCost for storage
        let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
        let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
        msgValue = msgValue - (storageFee + gasConsumption);

        // Only Owner of the this NFT Item can transfer it.
        require(ctx.sender == self.owner, "not owner");

        if (self.is_initialized == false) {  // Initial Transfer, aka the "Minting" of the NFT
            self.is_initialized = true;
            self.owner = msg.new_owner;
            send(SendParameters{
                to: msg.response_destination,
                value: 0,
                mode:  SendIgnoreErrors + SendRemainingValue,
                body: Excesses { query_id: msg.query_id }.toCell() //0xd53276db
            });
        } else {
            if (msg.forward_amount > 0) {
                send(SendParameters{
                    to: msg.new_owner,
                    value: msg.forward_amount,
                    mode: SendIgnoreErrors, 
                    bounce: false,
                    body: OwnershipAssigned{
                        query_id: msg.query_id,
                        prev_owner:  self.owner,
                        forward_payload: msg.forward_payload
                    }.toCell()
                }); 
            }
            msgValue = msgValue - ctx.readForwardFee(); 
            if (msg.response_destination != null) { 
                send(SendParameters{ 
                    to: msg.response_destination,
                    mode: SendIgnoreErrors,
                    value: msgValue,
                    body: Excesses { query_id: msg.query_id }.toCell() // 0xd53276db
                });
                self.owner = msg.new_owner; // change current owner to the new_owner
            } 
        }
    }
    
    receive(msg: GetStaticData){ 
        let ctx: Context = context();
        send(SendParameters {
            to: ctx.sender,
            value: 0,
            mode: 64,  // (return msg amount except gas fees) 
            bounce: true,
            body: ReportStaticData{
                query_id: msg.query_id,
                index_id: self.item_index,
                collection: self.owner
            }.toCell()
        });
    }

    // --------- Get Function  --------- //
    get fun get_nft_data(): GetNftData {
        let b: StringBuilder = beginString();
        let collectionData: String = self.individual_content.asSlice().asString();
        b.append(collectionData);
        b.append(self.item_index.toString());
        b.append(".json");

        return GetNftData {
            is_initialized: self.is_initialized, 
            index: self.item_index, 
            collection_address: self.collection_address, 
            owner_address: self.owner,
            individual_content: b.toCell()
        };
    }
}
// ---------------------------------------------------------------------------------------------------- //
// [Collection Contract] -> Transfer -> OwnershipAssigned -> NFTExcesses
message(0x5fcc3d14) Transfer { 
    query_id: Int as uint64;            
    new_owner: Address; 
    response_destination: Address; 
    custom_payload: Cell?; 
    forward_amount: Int as coins; 
    forward_payload: Slice as remaining; 
}

message(0x05138d91) OwnershipAssigned{
    query_id: Int as uint64;
    prev_owner: Address;
    forward_payload: Slice as remaining; 
}

message(0xd53276db) Excesses {
    query_id: Int as uint64;
}

message(0x2fcb26a2) GetStaticData { 
    query_id: Int as uint64;
}

message(0x8b771735) ReportStaticData{
    query_id: Int as uint64;
    index_id: Int;
    collection: Address;
}

struct GetNftData { 
    is_initialized: Bool;
    index: Int;
    collection_address: Address; 
    owner_address: Address;
    individual_content: Cell;
}`

@novusnota
Copy link
Contributor

novusnota commented Aug 28, 2023

@howardpen9 whoa, you've submitted quite a chunk of code :)

So, I took it, pasted it to the current ton-docs (into arbitrary article, just to showcase that the prism syntax is already merged there!) and applied my hacky two line fix of keyword highlighting:

Whew, I hope it's not too much.
Do you like how it looks? Because I do :)

@novusnota
Copy link
Contributor

novusnota commented Aug 31, 2023

After digging in the docs of Docusaurus, various color schemes and ton-docs, I now can make the proper change to the code highlighting theme in code blocks of TON documentation (as an extra bonus to this footstep :)

But first, let's collectively decide on the colors for Tact — please, pick between the two color options: left (closer to the theme in Tact docs), or right (closer to the original theme, Palenight), and I'll send the relevant PR to ton-docs.

Vote 🚀 for left, and 🎉 for right:

Tact, left Tact, right

And how FunC looks with adjustments made across the docs, same left 🚀 or right 🎉 vote:

FunC, left FunC, right

Note, that current Prism.js syntax of FunC & Fift regardless of what you choose above is quite incomplete. @SwiftAdviser, I'd volunteer to update those both for $200 as part of an another footstep, if necessary.

@howardpen9 @reveloper @Gusarich @jojudre @delovoyhomie

@delovoyhomie
Copy link
Collaborator

Rewards sent! Thank you for the contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved This proposal is approved by the committee footstep This is a TON Footstep issue
Projects
None yet
Development

No branches or pull requests

7 participants