package format import ( "slices" "sync" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" ) func getRules(context *formattingContext, rules []*ruleImpl) []*ruleImpl { bucket := getRulesMap()[getRuleBucketIndex(context.currentTokenSpan.Kind, context.nextTokenSpan.Kind)] if len(bucket) > 0 { ruleActionMask := ruleActionNone outer: for _, rule := range bucket { acceptRuleActions := ^getRuleActionExclusion(ruleActionMask) if rule.Action()&acceptRuleActions != 0 { preds := rule.Context() for _, p := range preds { if !p(context) { continue outer } } rules = append(rules, rule) ruleActionMask |= rule.Action() } } return rules } return rules } func getRuleBucketIndex(row ast.Kind, column ast.Kind) int { debug.Assert(row <= ast.KindLastKeyword && column <= ast.KindLastKeyword, "Must compute formatting context from tokens") return (int(row) * mapRowLength) + int(column) } const ( maskBitSize = 5 mask = 0b11111 // MaskBitSize bits mapRowLength = int(ast.KindLastToken) + 1 ) /** * For a given rule action, gets a mask of other rule actions that * cannot be applied at the same position. */ func getRuleActionExclusion(ruleAction ruleAction) ruleAction { mask := ruleActionNone if ruleAction&ruleActionStopProcessingSpaceActions != 0 { mask |= ruleActionModifySpaceAction } if ruleAction&ruleActionStopProcessingTokenActions != 0 { mask |= ruleActionModifyTokenAction } if ruleAction&ruleActionModifySpaceAction != 0 { mask |= ruleActionModifySpaceAction } if ruleAction&ruleActionModifyTokenAction != 0 { mask |= ruleActionModifyTokenAction } return mask } var getRulesMap = sync.OnceValue(buildRulesMap) func buildRulesMap() [][]*ruleImpl { rules := getAllRules() // Map from bucket index to array of rules m := make([][]*ruleImpl, mapRowLength*mapRowLength) // This array is used only during construction of the rulesbucket in the map rulesBucketConstructionStateList := make([]int, len(m)) for _, rule := range rules { specificRule := rule.leftTokenRange.isSpecific && rule.rightTokenRange.isSpecific for _, left := range rule.leftTokenRange.tokens { for _, right := range rule.rightTokenRange.tokens { index := getRuleBucketIndex(left, right) m[index] = addRule(m[index], rule.rule, specificRule, rulesBucketConstructionStateList, index) } } } return m } type RulesPosition int const ( RulesPositionStopRulesSpecific RulesPosition = 0 RulesPositionStopRulesAny RulesPosition = maskBitSize * 1 RulesPositionContextRulesSpecific RulesPosition = maskBitSize * 2 RulesPositionContextRulesAny RulesPosition = maskBitSize * 3 RulesPositionNoContextRulesSpecific RulesPosition = maskBitSize * 4 RulesPositionNoContextRulesAny RulesPosition = maskBitSize * 5 ) // The Rules list contains all the inserted rules into a rulebucket in the following order: // // 1- Ignore rules with specific token combination // 2- Ignore rules with any token combination // 3- Context rules with specific token combination // 4- Context rules with any token combination // 5- Non-context rules with specific token combination // 6- Non-context rules with any token combination // // The member rulesInsertionIndexBitmap is used to describe the number of rules // in each sub-bucket (above) hence can be used to know the index of where to insert // the next rule. It's a bitmap which contains 6 different sections each is given 5 bits. // // Example: // In order to insert a rule to the end of sub-bucket (3), we get the index by adding // the values in the bitmap segments 3rd, 2nd, and 1st. func addRule(rules []*ruleImpl, rule *ruleImpl, specificTokens bool, constructionState []int, rulesBucketIndex int) []*ruleImpl { var position RulesPosition if rule.Action()&ruleActionStopAction != 0 { if specificTokens { position = RulesPositionStopRulesSpecific } else { position = RulesPositionStopRulesAny } } else if len(rule.Context()) != 0 { if specificTokens { position = RulesPositionContextRulesSpecific } else { position = RulesPositionContextRulesAny } } else { if specificTokens { position = RulesPositionNoContextRulesSpecific } else { position = RulesPositionNoContextRulesAny } } state := constructionState[rulesBucketIndex] rules = slices.Insert(rules, getRuleInsertionIndex(state, position), rule) constructionState[rulesBucketIndex] = increaseInsertionIndex(state, position) return rules } func getRuleInsertionIndex(indexBitmap int, maskPosition RulesPosition) int { index := 0 for pos := 0; pos <= int(maskPosition); pos += maskBitSize { index += indexBitmap & mask indexBitmap >>= maskBitSize } return index } func increaseInsertionIndex(indexBitmap int, maskPosition RulesPosition) int { value := ((indexBitmap >> maskPosition) & mask) + 1 debug.Assert((value&mask) == value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.") return (indexBitmap & ^(mask << maskPosition)) | (value << maskPosition) }