@stacksjs/stx-vscode 0.2.545 → 0.2.546

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -807,6 +807,347 @@ var DEFAULT_TRANSITION_OPTIONS = {
807
807
  direction: "both" /* Both */
808
808
  };
809
809
 
810
+ // ../vscode/src/language-features.ts
811
+ var STX_DIRECTIVES = [
812
+ { name: "transition", description: "Applies transition animations to an element" },
813
+ { name: "motion", description: "Controls animation display based on user preferences" },
814
+ { name: "animationGroup", description: "Groups elements for coordinated animations" },
815
+ { name: "scrollAnimate", description: "Triggers animation when element scrolls into view" },
816
+ { name: "translate", description: "Translate a string using the i18n system" },
817
+ { name: "t", description: "Short alias for translate directive" },
818
+ { name: "if", description: "Conditional rendering" },
819
+ { name: "else", description: "Alternative rendering for conditionals" },
820
+ { name: "elseif", description: "Alternative condition for if statement" },
821
+ { name: "unless", description: "Inverse conditional rendering" },
822
+ { name: "foreach", description: "Iterates over an array or collection" },
823
+ { name: "for", description: "Standard for loop" },
824
+ { name: "while", description: "While loop" },
825
+ { name: "forelse", description: "Loop with fallback when collection is empty" },
826
+ { name: "switch", description: "Switch statement for multiple conditions" },
827
+ { name: "case", description: "Case option in switch statement" },
828
+ { name: "default", description: "Default case in switch statement" },
829
+ { name: "break", description: "Break from loop or switch" },
830
+ { name: "continue", description: "Continue to next iteration" },
831
+ { name: "component", description: "Renders a component" },
832
+ { name: "slot", description: "Define a named slot in a component" },
833
+ { name: "webcomponent", description: "Include a web component" },
834
+ { name: "ts", description: "TypeScript code block" },
835
+ { name: "js", description: "JavaScript code block" },
836
+ { name: "script", description: "Script code block (alternative to @ts)" },
837
+ { name: "css", description: "CSS code block" },
838
+ { name: "json", description: "Output variable as JSON" },
839
+ { name: "markdown", description: "Markdown content block" },
840
+ { name: "raw", description: "Display content without processing expressions" },
841
+ { name: "verbatim", description: "Display content without processing directives" },
842
+ { name: "extends", description: "Extend a base layout" },
843
+ { name: "section", description: "Define a content section" },
844
+ { name: "yield", description: "Output the contents of a section" },
845
+ { name: "parent", description: "Include parent section content" },
846
+ { name: "stack", description: "Define a stack for pushing content" },
847
+ { name: "push", description: "Push content to a stack" },
848
+ { name: "pushIf", description: "Conditionally push content to a stack" },
849
+ { name: "pushOnce", description: "Push content to a stack only once" },
850
+ { name: "prepend", description: "Prepend content to a stack" },
851
+ { name: "prependOnce", description: "Prepend content to a stack only once" },
852
+ { name: "include", description: "Include another template file" },
853
+ { name: "includeIf", description: "Conditionally include a template" },
854
+ { name: "includeWhen", description: "Include template when condition is true" },
855
+ { name: "includeUnless", description: "Include template unless condition is true" },
856
+ { name: "includeFirst", description: "Include the first existing template from a list" },
857
+ { name: "auth", description: "Content visible only to authenticated users" },
858
+ { name: "guest", description: "Content visible only to guests" },
859
+ { name: "can", description: "Check if user has permission" },
860
+ { name: "cannot", description: "Check if user lacks permission" },
861
+ { name: "elsecan", description: "Try another permission when the preceding authorization check fails" },
862
+ { name: "elsecannot", description: "Try another denied permission when the preceding authorization check fails" },
863
+ { name: "env", description: "Check environment condition" },
864
+ { name: "isset", description: "Check if a variable is set" },
865
+ { name: "empty", description: "Check if a variable is empty" },
866
+ { name: "error", description: "Display validation error for a field" },
867
+ { name: "hasSection", description: "Check if a section exists" },
868
+ { name: "once", description: "Include content only once per rendering cycle" },
869
+ { name: "use", description: "Import a class or namespace" },
870
+ { name: "csrf", description: "Include CSRF protection token" },
871
+ { name: "method", description: "Specify form submission method" },
872
+ { name: "locale", description: "Set locale for content" },
873
+ { name: "errorBoundary", description: "Catch rendering errors and show fallback content", snippet: `@errorBoundary
874
+ \${1}
875
+ @fallback
876
+ \${2}
877
+ @enderrorBoundary` },
878
+ { name: "fallback", description: "Define fallback content for an error, suspense, or stream boundary" },
879
+ { name: "suspense", description: "Coordinate async rendering with fallback content", snippet: `@suspense
880
+ \${1}
881
+ @fallback
882
+ \${2}
883
+ @endsuspense` },
884
+ { name: "async", description: "Load and render a component asynchronously", snippet: "@async(component: '${1:Component}', timeout: ${2:5000})\n\t${3}\n@endasync" },
885
+ { name: "defer", description: "Defer rendering until a browser trigger fires", snippet: "@defer(on: '${1:visible}')\n\t${2}\n@enddefer" },
886
+ { name: "teleport", description: "Render content into another DOM container", snippet: "@teleport('${1:#modals}')\n\t${2}\n@endteleport" },
887
+ { name: "keepAlive", description: "Preserve component state while it is inactive", snippet: `@keepAlive
888
+ \${1}
889
+ @endkeepAlive` },
890
+ { name: "virtualList", description: "Efficiently render a large scrolling list", snippet: "@virtualList(${1:items}, { itemHeight: ${2:50}, containerHeight: ${3:400} })\n\t${4}\n@endvirtualList" },
891
+ { name: "virtualGrid", description: "Efficiently render a large scrolling grid", snippet: "@virtualGrid(${1:items}, { columns: ${2:4}, rowHeight: ${3:200}, containerHeight: ${4:600} })\n\t${5}\n@endvirtualGrid" },
892
+ { name: "infiniteList", description: "Render a virtual list that loads additional items", snippet: "@infiniteList(${1:items}, { itemHeight: ${2:50} })\n\t${3}\n@loading\n\t${4}\n@endinfiniteList" },
893
+ { name: "loading", description: "Define loading content for an infinite list" },
894
+ { name: "stream", description: "Stream a server-rendered boundary independently", snippet: `@stream('\${1:content}')
895
+ \${2}
896
+ @fallback
897
+ \${3}
898
+ @endstream` },
899
+ { name: "memo", description: "Skip subtree updates while dependencies are unchanged", snippet: "@memo([${1:dependencies}])\n\t${2}\n@endmemo" },
900
+ { name: "client:load", description: "Hydrate an island immediately on page load", snippet: `@client:load
901
+ \${1}
902
+ @endclient` },
903
+ { name: "client:idle", description: "Hydrate an island when the browser is idle", snippet: `@client:idle
904
+ \${1}
905
+ @endclient` },
906
+ { name: "client:visible", description: "Hydrate an island when it enters the viewport", snippet: `@client:visible
907
+ \${1}
908
+ @endclient` },
909
+ { name: "client:media", description: "Hydrate an island when a media query matches", snippet: "@client:media(media: '${1:(min-width: 768px)}')\n\t${2}\n@endclient" },
910
+ { name: "client:only", description: "Render an island only in the browser", snippet: `@client:only
911
+ \${1}
912
+ @endclient` },
913
+ { name: "client:hover", description: "Hydrate an island on its first hover or focus", snippet: `@client:hover
914
+ \${1}
915
+ @endclient` },
916
+ { name: "client:event", description: "Hydrate an island when a custom event fires", snippet: "@client:event(event: '${1:event-name}')\n\t${2}\n@endclient" },
917
+ { name: "static", description: "Render content without a hydration wrapper", snippet: `@static
918
+ \${1}
919
+ @endstatic` },
920
+ { name: "computed", description: "Declare a computed template value", snippet: "@computed(${1:name}, ${2:expression})" },
921
+ { name: "watch", description: "Watch a value and run a handler when it changes", snippet: "@watch(${1:source})\n\t${2}\n@endwatch" },
922
+ { name: "errors", description: "Render all validation errors for a field", snippet: "@errors(${1:form.field})\n\t${2}\n@enderrors" },
923
+ { name: "hasErrors", description: "Conditionally render when a field has validation errors", snippet: "@hasErrors(${1:form.field})\n\t${2}\n@endhasErrors" },
924
+ { name: "head", description: "Add markup to the document head", snippet: `@head
925
+ \${1}
926
+ @endhead` },
927
+ { name: "title", description: "Set the document title", snippet: "@title('${1:Page title}')" },
928
+ { name: "meta", description: "Add a metadata tag", snippet: "@meta('${1:description}', '${2:content}')" },
929
+ { name: "metaTag", description: "Add a metadata tag with full attribute control", snippet: "@metaTag({ name: '${1:description}', content: '${2:content}' })" },
930
+ { name: "seo", description: "Generate title, meta, Open Graph, and Twitter tags", snippet: "@seo({ title: ${1:title}, description: ${2:description} })" },
931
+ { name: "structuredData", description: "Emit JSON-LD structured data", snippet: "@structuredData(${1:schema})" },
932
+ { name: "csp", description: "Emit a Content Security Policy meta tag", snippet: "@csp" },
933
+ { name: "cspNonce", description: "Output the current Content Security Policy nonce", snippet: "@cspNonce" },
934
+ { name: "endtransition", description: "Ends a transition block" },
935
+ { name: "endmotion", description: "Ends a motion block" },
936
+ { name: "endscrollAnimate", description: "Ends a scroll animation block" },
937
+ { name: "endif", description: "Ends an if block" },
938
+ { name: "endunless", description: "Ends an unless block" },
939
+ { name: "endfor", description: "Ends a for loop block" },
940
+ { name: "endforeach", description: "Ends a foreach loop block" },
941
+ { name: "endforelse", description: "Ends a forelse block" },
942
+ { name: "endwhile", description: "Ends a while block" },
943
+ { name: "endswitch", description: "Ends a switch block" },
944
+ { name: "endts", description: "Ends a TypeScript code block" },
945
+ { name: "endjs", description: "Ends a JavaScript code block" },
946
+ { name: "endscript", description: "Ends a script block" },
947
+ { name: "endcss", description: "Ends a CSS block" },
948
+ { name: "endmarkdown", description: "Ends a markdown block" },
949
+ { name: "endraw", description: "Ends a raw content block" },
950
+ { name: "endverbatim", description: "Ends a verbatim block" },
951
+ { name: "endcomponent", description: "Ends a component block" },
952
+ { name: "endslot", description: "Ends a slot block" },
953
+ { name: "endsection", description: "Ends a section block" },
954
+ { name: "endpush", description: "Ends a push block" },
955
+ { name: "endpushIf", description: "Ends a pushIf block" },
956
+ { name: "endpushOnce", description: "Ends a pushOnce block" },
957
+ { name: "endprepend", description: "Ends a prepend block" },
958
+ { name: "endprependOnce", description: "Ends a prependOnce block" },
959
+ { name: "endauth", description: "Ends an auth block" },
960
+ { name: "endguest", description: "Ends a guest block" },
961
+ { name: "endcan", description: "Ends a can block" },
962
+ { name: "endcannot", description: "Ends a cannot block" },
963
+ { name: "endempty", description: "Ends an empty block" },
964
+ { name: "endenv", description: "Ends an env block" },
965
+ { name: "endisset", description: "Ends an isset block" },
966
+ { name: "enderror", description: "Ends an error block" },
967
+ { name: "endonce", description: "Ends a once block" },
968
+ { name: "endwebcomponent", description: "Ends a webcomponent block" },
969
+ { name: "enderrorBoundary", description: "Ends an error boundary" },
970
+ { name: "endsuspense", description: "Ends a suspense boundary" },
971
+ { name: "endasync", description: "Ends an async component block" },
972
+ { name: "enddefer", description: "Ends a deferred block" },
973
+ { name: "endteleport", description: "Ends a teleported block" },
974
+ { name: "endkeepAlive", description: "Ends a keep-alive block" },
975
+ { name: "endvirtualList", description: "Ends a virtual list" },
976
+ { name: "endvirtualGrid", description: "Ends a virtual grid" },
977
+ { name: "endinfiniteList", description: "Ends an infinite list" },
978
+ { name: "endstream", description: "Ends a streamed boundary" },
979
+ { name: "endmemo", description: "Ends a memoized block" },
980
+ { name: "endclient", description: "Ends a partially hydrated island" },
981
+ { name: "endstatic", description: "Ends a static block" },
982
+ { name: "endwatch", description: "Ends a watch block" },
983
+ { name: "enderrors", description: "Ends a validation errors block" },
984
+ { name: "endhasErrors", description: "Ends a validation condition" },
985
+ { name: "endhead", description: "Ends a document head block" }
986
+ ];
987
+ var STX_DIRECTIVE_PAIRS = {
988
+ if: "endif",
989
+ unless: "endunless",
990
+ for: "endfor",
991
+ foreach: "endforeach",
992
+ forelse: "endforelse",
993
+ while: "endwhile",
994
+ switch: "endswitch",
995
+ ts: "endts",
996
+ js: "endjs",
997
+ script: "endscript",
998
+ css: "endcss",
999
+ markdown: "endmarkdown",
1000
+ raw: "endraw",
1001
+ verbatim: "endverbatim",
1002
+ component: "endcomponent",
1003
+ slot: "endslot",
1004
+ webcomponent: "endwebcomponent",
1005
+ section: "endsection",
1006
+ push: "endpush",
1007
+ pushIf: "endpushIf",
1008
+ pushOnce: "endpushOnce",
1009
+ prepend: "endprepend",
1010
+ prependOnce: "endprependOnce",
1011
+ auth: "endauth",
1012
+ guest: "endguest",
1013
+ can: "endcan",
1014
+ cannot: "endcannot",
1015
+ env: "endenv",
1016
+ isset: "endisset",
1017
+ empty: "endempty",
1018
+ error: "enderror",
1019
+ once: "endonce",
1020
+ transition: "endtransition",
1021
+ motion: "endmotion",
1022
+ scrollAnimate: "endscrollAnimate",
1023
+ errorBoundary: "enderrorBoundary",
1024
+ suspense: "endsuspense",
1025
+ async: "endasync",
1026
+ defer: "enddefer",
1027
+ teleport: "endteleport",
1028
+ keepAlive: "endkeepAlive",
1029
+ virtualList: "endvirtualList",
1030
+ virtualGrid: "endvirtualGrid",
1031
+ infiniteList: "endinfiniteList",
1032
+ stream: "endstream",
1033
+ memo: "endmemo",
1034
+ static: "endstatic",
1035
+ watch: "endwatch",
1036
+ errors: "enderrors",
1037
+ hasErrors: "endhasErrors",
1038
+ head: "endhead"
1039
+ };
1040
+ var STX_OPENING_DIRECTIVE_BY_CLOSE = new Map(Object.entries(STX_DIRECTIVE_PAIRS).map(([opening, closing]) => [closing, opening]));
1041
+ var STX_DIRECTIVE_BY_NAME = new Map(STX_DIRECTIVES.map((directive) => [directive.name.toLowerCase(), directive]));
1042
+ function getStxDirectiveAtOffset(line, offset) {
1043
+ const directivePattern = /@([A-Za-z][\w:]*)/g;
1044
+ let match;
1045
+ while ((match = directivePattern.exec(line)) !== null) {
1046
+ const start = match.index;
1047
+ const end = start + match[0].length;
1048
+ if (offset >= start && offset <= end)
1049
+ return { name: match[1], start, end };
1050
+ }
1051
+ return;
1052
+ }
1053
+ var STX_ATTRIBUTE_DIRECTIVES = [
1054
+ { name: ":if", description: "Conditionally render this element", snippet: ':if="${1:condition}"' },
1055
+ { name: ":else-if", description: "Render when the preceding condition is false and this condition is true", snippet: ':else-if="${1:condition}"' },
1056
+ { name: ":else", description: "Render when the preceding conditions are false", snippet: ":else" },
1057
+ { name: ":for", description: "Render this element for every item in a reactive collection", snippet: ':for="${1:item} in ${2:items}"' },
1058
+ { name: ":show", description: "Toggle this element without removing it from the DOM", snippet: ':show="${1:condition}"' },
1059
+ { name: ":key", description: "Provide a stable identity for a rendered item", snippet: ':key="${1:item.id}"' },
1060
+ { name: "v-if", description: "Vue-compatible alias for :if", snippet: 'v-if="${1:condition}"' },
1061
+ { name: "v-else-if", description: "Vue-compatible alias for :else-if", snippet: 'v-else-if="${1:condition}"' },
1062
+ { name: "v-else", description: "Vue-compatible alias for :else", snippet: "v-else" },
1063
+ { name: "v-memo", description: "Skip subtree updates while dependencies are unchanged", snippet: 'v-memo="[${1:dependencies}]"' },
1064
+ { name: "x-if", description: "Alpine-compatible alias for :if", snippet: 'x-if="${1:condition}"' },
1065
+ { name: "x-else-if", description: "Alpine-compatible alias for :else-if", snippet: 'x-else-if="${1:condition}"' },
1066
+ { name: "x-else", description: "Alpine-compatible alias for :else", snippet: "x-else" },
1067
+ { name: "x-for", description: "Alpine-compatible alias for :for", snippet: 'x-for="${1:item} in ${2:items}"' },
1068
+ { name: "x-show", description: "Alpine-compatible alias for :show", snippet: 'x-show="${1:condition}"' },
1069
+ { name: "x-text", description: "Set text content from a reactive expression", snippet: 'x-text="${1:expression}"' },
1070
+ { name: "x-html", description: "Set HTML content from a reactive expression", snippet: 'x-html="${1:expression}"' },
1071
+ { name: "x-model", description: "Two-way bind a form control to reactive state", snippet: 'x-model="${1:value}"' },
1072
+ { name: "x-class", description: "Bind classes from a reactive expression", snippet: 'x-class="${1:classes}"' },
1073
+ { name: "x-style", description: "Bind inline styles from a reactive expression", snippet: 'x-style="${1:styles}"' },
1074
+ { name: "x-href", description: "Bind an anchor destination", snippet: 'x-href="${1:url}"' },
1075
+ { name: "x-src", description: "Bind an image or media source", snippet: 'x-src="${1:url}"' },
1076
+ { name: "x-bind:", description: "Bind any HTML attribute", snippet: 'x-bind:${1:attribute}="${2:expression}"' },
1077
+ { name: "x-ref", description: "Register a named element reference", snippet: 'x-ref="${1:name}"' },
1078
+ { name: "x-cloak", description: "Hide an element until its reactive scope is ready", snippet: "x-cloak" },
1079
+ { name: "@click", description: "Run an expression when the element is clicked", snippet: '@click="${1:handler}"' },
1080
+ { name: "@submit", description: "Run an expression when the form is submitted", snippet: '@submit.prevent="${1:handler}"' },
1081
+ { name: "@input", description: "Run an expression when input is received", snippet: '@input="${1:handler}"' },
1082
+ { name: "@change", description: "Run an expression when the value changes", snippet: '@change="${1:handler}"' },
1083
+ { name: "@keydown", description: "Run an expression on keydown", snippet: '@keydown="${1:handler}"' },
1084
+ { name: "@keyup", description: "Run an expression on keyup", snippet: '@keyup="${1:handler}"' },
1085
+ { name: "@focus", description: "Run an expression when the element receives focus", snippet: '@focus="${1:handler}"' },
1086
+ { name: "@blur", description: "Run an expression when the element loses focus", snippet: '@blur="${1:handler}"' },
1087
+ { name: "@mouseenter", description: "Run an expression when the pointer enters the element", snippet: '@mouseenter="${1:handler}"' },
1088
+ { name: "@mouseleave", description: "Run an expression when the pointer leaves the element", snippet: '@mouseleave="${1:handler}"' },
1089
+ { name: "stx-hydrate", description: "Defer reactive hydration until a trigger fires", snippet: 'stx-hydrate="${1:visible}"' }
1090
+ ];
1091
+ var STX_ATTRIBUTE_DIRECTIVE_BY_NAME = new Map(STX_ATTRIBUTE_DIRECTIVES.map((directive) => [directive.name.toLowerCase(), directive]));
1092
+ function getStxAttributeCompletionContext(sourceBeforeCursor) {
1093
+ let tagStart = -1;
1094
+ let quote;
1095
+ for (let index = 0;index < sourceBeforeCursor.length; index++) {
1096
+ const character = sourceBeforeCursor[index];
1097
+ if (quote) {
1098
+ if (character === quote && sourceBeforeCursor[index - 1] !== "\\")
1099
+ quote = undefined;
1100
+ continue;
1101
+ }
1102
+ if ((character === '"' || character === "'") && tagStart >= 0) {
1103
+ quote = character;
1104
+ continue;
1105
+ }
1106
+ if (character === "<") {
1107
+ const next = sourceBeforeCursor[index + 1];
1108
+ tagStart = next && /[A-Za-z]/.test(next) ? index : -1;
1109
+ } else if (character === ">") {
1110
+ tagStart = -1;
1111
+ }
1112
+ }
1113
+ if (tagStart < 0 || quote)
1114
+ return;
1115
+ const tag = sourceBeforeCursor.slice(tagStart + 1);
1116
+ const match = tag.match(/\s([:@\w.-]*)$/);
1117
+ if (!match)
1118
+ return;
1119
+ return { prefix: match[1] };
1120
+ }
1121
+ function getStxAttributeAtOffset(source, offset) {
1122
+ const attributePattern = /(?:^|\s)([:@][A-Za-z][\w:.-]*|(?:x|v|stx)-[A-Za-z][\w:.-]*)/g;
1123
+ let match;
1124
+ while ((match = attributePattern.exec(source)) !== null) {
1125
+ const name = match[1];
1126
+ const start = match.index + match[0].length - name.length;
1127
+ const end = start + name.length;
1128
+ if (offset < start || offset > end)
1129
+ continue;
1130
+ const completionContext = getStxAttributeCompletionContext(source.slice(0, end));
1131
+ if (completionContext?.prefix === name)
1132
+ return { name, start, end };
1133
+ }
1134
+ return;
1135
+ }
1136
+ function getStxAttributeDefinition(name) {
1137
+ const normalized = name.toLowerCase();
1138
+ const exact = STX_ATTRIBUTE_DIRECTIVE_BY_NAME.get(normalized);
1139
+ if (exact)
1140
+ return exact;
1141
+ if (normalized.startsWith("@"))
1142
+ return STX_ATTRIBUTE_DIRECTIVE_BY_NAME.get(normalized.split(".")[0]);
1143
+ if (normalized.startsWith("x-bind:"))
1144
+ return STX_ATTRIBUTE_DIRECTIVE_BY_NAME.get("x-bind:");
1145
+ return;
1146
+ }
1147
+ function stxSnippetPreview(snippet) {
1148
+ return snippet.replace(/\$\{\d+\|([^|]+)\|\}/g, (_match, choices) => choices.split(",")[0].replace(/\\,/g, ",")).replace(/\$\{\d+:([^}]*)\}/g, "$1").replace(/\$\{\d+\}/g, "").replace(/\$\d+/g, "");
1149
+ }
1150
+
810
1151
  // ../vscode/src/services/ComponentRegistry.ts
811
1152
  import * as vscode from "vscode";
812
1153
  import * as path from "node:path";
@@ -1388,6 +1729,111 @@ ${formattedCode}
1388
1729
  `);
1389
1730
  }
1390
1731
 
1732
+ // ../vscode/src/providers/hover-documentation.ts
1733
+ var JAVASCRIPT_OPERATOR_HOVERS = {
1734
+ typeof: {
1735
+ syntax: "typeof operand",
1736
+ description: '**JavaScript Operator**\n\nReturns a string indicating the type of the operand.\n\n**Possible return values:**\n- `"string"` - for string values\n- `"number"` - for number values\n- `"boolean"` - for boolean values\n- `"object"` - for objects and null\n- `"function"` - for functions\n- `"undefined"` - for undefined\n- `"symbol"` - for symbols\n- `"bigint"` - for BigInt values'
1737
+ },
1738
+ instanceof: {
1739
+ syntax: "object instanceof Constructor",
1740
+ description: "**JavaScript Operator**\n\nTests whether an object has in its prototype chain the prototype property of a constructor. Returns `true` or `false`."
1741
+ },
1742
+ delete: {
1743
+ syntax: "delete object.property",
1744
+ description: "**JavaScript Operator**\n\nRemoves a property from an object. Returns `true` if successful."
1745
+ },
1746
+ in: {
1747
+ syntax: '"property" in object',
1748
+ description: "**JavaScript Operator**\n\nReturns `true` if the specified property is in the specified object or its prototype chain."
1749
+ },
1750
+ of: {
1751
+ syntax: "for (const item of iterable)",
1752
+ description: "**JavaScript Keyword**\n\nUsed in `for...of` loops to iterate over iterable objects (Arrays, Strings, Maps, Sets, etc.)."
1753
+ }
1754
+ };
1755
+ var GLOBAL_OBJECT_HOVERS = {
1756
+ window: { type: "Window & typeof globalThis", description: "The global window object represents the browser window. Provides access to the DOM, browser APIs, and global variables." },
1757
+ document: { type: "Document", description: "The Document interface represents the web page loaded in the browser. Entry point to the DOM tree." },
1758
+ console: { type: "Console", description: "Provides access to the browser's debugging console with methods like `log()`, `error()`, `warn()`, etc." },
1759
+ navigator: { type: "Navigator", description: "Represents the state and identity of the user agent (browser). Provides information about the browser and device." },
1760
+ location: { type: "Location", description: "Represents the URL of the current document and provides methods to manipulate the browser's location." },
1761
+ localStorage: { type: "Storage", description: "Provides access to the browser's local storage. Data persists even after the browser is closed." },
1762
+ sessionStorage: { type: "Storage", description: "Provides access to the browser's session storage. Data is cleared when the page session ends." },
1763
+ history: { type: "History", description: "Provides an interface for manipulating the browser session history (pages visited in the tab/frame)." },
1764
+ Math: { type: "Math", description: "Built-in object with properties and methods for mathematical constants and functions." },
1765
+ JSON: { type: "JSON", description: "Provides methods for parsing JSON and converting values to JSON: `parse()` and `stringify()`." },
1766
+ globalThis: { type: "typeof globalThis", description: "Provides a standard way to access the global object across different JavaScript environments." }
1767
+ };
1768
+ var HTML_TAGS = new Set([
1769
+ "div",
1770
+ "span",
1771
+ "p",
1772
+ "h1",
1773
+ "h2",
1774
+ "h3",
1775
+ "h4",
1776
+ "h5",
1777
+ "h6",
1778
+ "strong",
1779
+ "em",
1780
+ "a",
1781
+ "img",
1782
+ "button",
1783
+ "input",
1784
+ "form",
1785
+ "label",
1786
+ "ul",
1787
+ "ol",
1788
+ "li",
1789
+ "table",
1790
+ "tr",
1791
+ "td",
1792
+ "th",
1793
+ "thead",
1794
+ "tbody",
1795
+ "select",
1796
+ "option",
1797
+ "textarea",
1798
+ "header",
1799
+ "footer",
1800
+ "nav",
1801
+ "main",
1802
+ "body",
1803
+ "section",
1804
+ "article",
1805
+ "aside",
1806
+ "code",
1807
+ "pre",
1808
+ "hr",
1809
+ "br"
1810
+ ]);
1811
+ var HTML_TAG_DESCRIPTIONS = {
1812
+ body: "Main content of the HTML document. Contains all content that is visible to the user.",
1813
+ div: "Generic container element for flow content with no semantic meaning.",
1814
+ span: "Generic inline container with no semantic meaning. Used for styling or grouping text.",
1815
+ p: "Paragraph element representing a block of text.",
1816
+ strong: "Indicates strong importance. Text is typically displayed in bold.",
1817
+ em: "Marks text that has stress emphasis. Text is typically displayed in italic.",
1818
+ a: "Anchor element for creating hyperlinks to other pages, files, or locations.",
1819
+ img: "Embeds an image into the document. Self-closing tag requiring src attribute.",
1820
+ button: "Clickable button element for forms or interactive elements.",
1821
+ input: "Form input element for user data entry. Requires type attribute.",
1822
+ form: "Container for a form with inputs and controls for user submission.",
1823
+ ul: "Unordered list container. Contains li elements.",
1824
+ ol: "Ordered (numbered) list container. Contains li elements.",
1825
+ li: "List item element. Used within ul or ol containers.",
1826
+ table: "Container for tabular data with rows and columns.",
1827
+ pre: "Preformatted text element. Preserves whitespace and line breaks.",
1828
+ code: "Represents computer code within a document.",
1829
+ label: "Label for a form element. Improves accessibility and usability."
1830
+ };
1831
+ function htmlTagDescription(tag) {
1832
+ if (/^h[1-6]$/.test(tag))
1833
+ return `Level ${tag.slice(1)} heading element. H1 is most important, H6 is least important.`;
1834
+ return HTML_TAG_DESCRIPTIONS[tag] ?? `HTML ${tag} element`;
1835
+ }
1836
+
1391
1837
  // ../vscode/src/providers/hoverProvider.ts
1392
1838
  function appendDiagnosticInfo(hover, document, position) {
1393
1839
  const diagnostics = vscode2.languages.getDiagnostics(document.uri);
@@ -1442,6 +1888,23 @@ function createHoverProvider(virtualTsDocumentProvider) {
1442
1888
  return null;
1443
1889
  }
1444
1890
  const word = document.getText(wordRange);
1891
+ const source = document.getText();
1892
+ const sourceOffset = document.offsetAt(position);
1893
+ const attributeContext = getStxAttributeAtOffset(source, sourceOffset);
1894
+ if (attributeContext) {
1895
+ const definition = getStxAttributeDefinition(attributeContext.name);
1896
+ if (definition) {
1897
+ const hover = new vscode2.MarkdownString;
1898
+ hover.appendMarkdown(definition.description);
1899
+ hover.appendMarkdown(`
1900
+
1901
+ **Syntax**
1902
+ `);
1903
+ hover.appendCodeblock(stxSnippetPreview(definition.snippet), "stx");
1904
+ const range = new vscode2.Range(document.positionAt(attributeContext.start), document.positionAt(attributeContext.end));
1905
+ return createHoverWithDiagnostics(hover, document, position, range);
1906
+ }
1907
+ }
1445
1908
  const line = document.lineAt(position.line).text;
1446
1909
  const textBeforeWord = document.getText(new vscode2.Range(new vscode2.Position(position.line, 0), wordRange.start));
1447
1910
  const propertyAccessMatch = textBeforeWord.match(/(\w+)\.$/);
@@ -1520,100 +1983,20 @@ ${propDetail.description}`);
1520
1983
  }
1521
1984
  }
1522
1985
  }
1523
- const jsOperators = ["typeof", "instanceof", "delete", "in", "of"];
1524
- if (jsOperators.includes(word)) {
1986
+ const operatorDocumentation = JAVASCRIPT_OPERATOR_HOVERS[word];
1987
+ if (operatorDocumentation) {
1525
1988
  const hover = new vscode2.MarkdownString;
1526
1989
  hover.isTrusted = true;
1527
1990
  hover.supportHtml = true;
1528
- let description = "";
1529
- switch (word) {
1530
- case "typeof":
1531
- hover.appendCodeblock("typeof operand", "typescript");
1532
- hover.appendText(`
1533
-
1534
- `);
1535
- description = '**JavaScript Operator**\n\nReturns a string indicating the type of the operand.\n\n**Possible return values:**\n- `"string"` - for string values\n- `"number"` - for number values\n- `"boolean"` - for boolean values\n- `"object"` - for objects and null\n- `"function"` - for functions\n- `"undefined"` - for undefined\n- `"symbol"` - for symbols\n- `"bigint"` - for BigInt values';
1536
- break;
1537
- case "instanceof":
1538
- hover.appendCodeblock("object instanceof Constructor", "typescript");
1539
- hover.appendText(`
1540
-
1541
- `);
1542
- description = "**JavaScript Operator**\n\nTests whether an object has in its prototype chain the prototype property of a constructor. Returns `true` or `false`.";
1543
- break;
1544
- case "delete":
1545
- hover.appendCodeblock("delete object.property", "typescript");
1546
- hover.appendText(`
1547
-
1548
- `);
1549
- description = "**JavaScript Operator**\n\nRemoves a property from an object. Returns `true` if successful.";
1550
- break;
1551
- case "in":
1552
- hover.appendCodeblock('"property" in object', "typescript");
1553
- hover.appendText(`
1554
-
1555
- `);
1556
- description = "**JavaScript Operator**\n\nReturns `true` if the specified property is in the specified object or its prototype chain.";
1557
- break;
1558
- case "of":
1559
- hover.appendCodeblock("for (const item of iterable)", "typescript");
1560
- hover.appendText(`
1991
+ hover.appendCodeblock(operatorDocumentation.syntax, "typescript");
1992
+ hover.appendText(`
1561
1993
 
1562
1994
  `);
1563
- description = "**JavaScript Keyword**\n\nUsed in `for...of` loops to iterate over iterable objects (Arrays, Strings, Maps, Sets, etc.).";
1564
- break;
1565
- }
1566
- hover.appendMarkdown(description);
1995
+ hover.appendMarkdown(operatorDocumentation.description);
1567
1996
  return createHoverWithDiagnostics(hover, document, position);
1568
1997
  }
1569
- const globalObjects = {
1570
- window: {
1571
- type: "Window & typeof globalThis",
1572
- description: "The global window object represents the browser window. Provides access to the DOM, browser APIs, and global variables."
1573
- },
1574
- document: {
1575
- type: "Document",
1576
- description: "The Document interface represents the web page loaded in the browser. Entry point to the DOM tree."
1577
- },
1578
- console: {
1579
- type: "Console",
1580
- description: "Provides access to the browser's debugging console with methods like `log()`, `error()`, `warn()`, etc."
1581
- },
1582
- navigator: {
1583
- type: "Navigator",
1584
- description: "Represents the state and identity of the user agent (browser). Provides information about the browser and device."
1585
- },
1586
- location: {
1587
- type: "Location",
1588
- description: "Represents the URL of the current document and provides methods to manipulate the browser's location."
1589
- },
1590
- localStorage: {
1591
- type: "Storage",
1592
- description: "Provides access to the browser's local storage. Data persists even after the browser is closed."
1593
- },
1594
- sessionStorage: {
1595
- type: "Storage",
1596
- description: "Provides access to the browser's session storage. Data is cleared when the page session ends."
1597
- },
1598
- history: {
1599
- type: "History",
1600
- description: "Provides an interface for manipulating the browser session history (pages visited in the tab/frame)."
1601
- },
1602
- Math: {
1603
- type: "Math",
1604
- description: "Built-in object with properties and methods for mathematical constants and functions."
1605
- },
1606
- JSON: {
1607
- type: "JSON",
1608
- description: "Provides methods for parsing JSON and converting values to JSON: `parse()` and `stringify()`."
1609
- },
1610
- globalThis: {
1611
- type: "typeof globalThis",
1612
- description: "Provides a standard way to access the global object across different JavaScript environments."
1613
- }
1614
- };
1615
- if (word in globalObjects) {
1616
- const global = globalObjects[word];
1998
+ const global = GLOBAL_OBJECT_HOVERS[word];
1999
+ if (global) {
1617
2000
  const hover = new vscode2.MarkdownString;
1618
2001
  hover.isTrusted = true;
1619
2002
  hover.supportHtml = true;
@@ -1968,8 +2351,7 @@ ${cssStyles.join(`
1968
2351
  const htmlTagOpening = line.includes(`<${word}`);
1969
2352
  const htmlTagClosing = line.includes(`</${word}`);
1970
2353
  const isHtmlTag = htmlTagOpening || htmlTagClosing || line.match(new RegExp(`<${word}[\\s>]`)) !== null;
1971
- const htmlTags = ["div", "span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "a", "img", "button", "input", "form", "label", "ul", "ol", "li", "table", "tr", "td", "th", "thead", "tbody", "select", "option", "textarea", "header", "footer", "nav", "main", "body", "section", "article", "aside", "code", "pre", "hr", "br"];
1972
- if (htmlTags.includes(word) && isHtmlTag) {
2354
+ if (HTML_TAGS.has(word) && isHtmlTag) {
1973
2355
  const hover = new vscode2.MarkdownString;
1974
2356
  hover.isTrusted = true;
1975
2357
  hover.supportHtml = true;
@@ -1977,75 +2359,7 @@ ${cssStyles.join(`
1977
2359
  hover.appendText(`
1978
2360
 
1979
2361
  `);
1980
- let tagDescription = "";
1981
- switch (word) {
1982
- case "body":
1983
- tagDescription = "Main content of the HTML document. Contains all content that is visible to the user.";
1984
- break;
1985
- case "div":
1986
- tagDescription = "Generic container element for flow content with no semantic meaning.";
1987
- break;
1988
- case "span":
1989
- tagDescription = "Generic inline container with no semantic meaning. Used for styling or grouping text.";
1990
- break;
1991
- case "p":
1992
- tagDescription = "Paragraph element representing a block of text.";
1993
- break;
1994
- case "h1":
1995
- case "h2":
1996
- case "h3":
1997
- case "h4":
1998
- case "h5":
1999
- case "h6":
2000
- tagDescription = `Level ${word.substring(1)} heading element. H1 is most important, H6 is least important.`;
2001
- break;
2002
- case "strong":
2003
- tagDescription = "Indicates strong importance. Text is typically displayed in bold.";
2004
- break;
2005
- case "em":
2006
- tagDescription = "Marks text that has stress emphasis. Text is typically displayed in italic.";
2007
- break;
2008
- case "a":
2009
- tagDescription = "Anchor element for creating hyperlinks to other pages, files, or locations.";
2010
- break;
2011
- case "img":
2012
- tagDescription = "Embeds an image into the document. Self-closing tag requiring src attribute.";
2013
- break;
2014
- case "button":
2015
- tagDescription = "Clickable button element for forms or interactive elements.";
2016
- break;
2017
- case "input":
2018
- tagDescription = "Form input element for user data entry. Requires type attribute.";
2019
- break;
2020
- case "form":
2021
- tagDescription = "Container for a form with inputs and controls for user submission.";
2022
- break;
2023
- case "ul":
2024
- tagDescription = "Unordered list container. Contains li elements.";
2025
- break;
2026
- case "ol":
2027
- tagDescription = "Ordered (numbered) list container. Contains li elements.";
2028
- break;
2029
- case "li":
2030
- tagDescription = "List item element. Used within ul or ol containers.";
2031
- break;
2032
- case "table":
2033
- tagDescription = "Container for tabular data with rows and columns.";
2034
- break;
2035
- case "pre":
2036
- tagDescription = "Preformatted text element. Preserves whitespace and line breaks.";
2037
- break;
2038
- case "code":
2039
- tagDescription = "Represents computer code within a document.";
2040
- break;
2041
- case "label":
2042
- tagDescription = "Label for a form element. Improves accessibility and usability.";
2043
- break;
2044
- default:
2045
- tagDescription = `HTML ${word} element`;
2046
- break;
2047
- }
2048
- hover.appendMarkdown(tagDescription);
2362
+ hover.appendMarkdown(htmlTagDescription(word));
2049
2363
  hover.appendMarkdown(`
2050
2364
 
2051
2365
  [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${word})`);
@@ -2075,120 +2389,15 @@ ${cssStyles.join(`
2075
2389
  }
2076
2390
  }
2077
2391
  }
2078
- const atPosition = line.indexOf("@");
2079
- const wordAfterAt = atPosition >= 0 && position.character > atPosition;
2080
- const directiveMatch = line.match(/@(\w+)/);
2081
- const possibleDirective = directiveMatch ? directiveMatch[1] : null;
2082
- const isAtOrDirective = word.startsWith("@") || word === possibleDirective || wordAfterAt;
2392
+ const directiveContext = getStxDirectiveAtOffset(line, position.character);
2393
+ const isAtOrDirective = directiveContext !== undefined;
2083
2394
  if (isAtOrDirective) {
2084
- let directiveName = word;
2085
- if (word.startsWith("@")) {
2086
- directiveName = word.substring(1);
2087
- } else if (wordAfterAt && possibleDirective === word) {
2088
- directiveName = word;
2089
- }
2395
+ const directiveName = directiveContext.name;
2090
2396
  if (directiveName.length === 0 || directiveName === "@") {
2091
2397
  return null;
2092
2398
  }
2093
- const validDirectives = [
2094
- "if",
2095
- "else",
2096
- "elseif",
2097
- "elif",
2098
- "endif",
2099
- "unless",
2100
- "endunless",
2101
- "for",
2102
- "endfor",
2103
- "foreach",
2104
- "endforeach",
2105
- "forelse",
2106
- "endforelse",
2107
- "while",
2108
- "endwhile",
2109
- "switch",
2110
- "case",
2111
- "default",
2112
- "endswitch",
2113
- "break",
2114
- "continue",
2115
- "component",
2116
- "endcomponent",
2117
- "slot",
2118
- "endslot",
2119
- "webcomponent",
2120
- "endwebcomponent",
2121
- "ts",
2122
- "endts",
2123
- "js",
2124
- "endjs",
2125
- "script",
2126
- "endscript",
2127
- "css",
2128
- "endcss",
2129
- "json",
2130
- "markdown",
2131
- "endmarkdown",
2132
- "raw",
2133
- "endraw",
2134
- "verbatim",
2135
- "endverbatim",
2136
- "t",
2137
- "translate",
2138
- "extends",
2139
- "section",
2140
- "endsection",
2141
- "yield",
2142
- "parent",
2143
- "stack",
2144
- "push",
2145
- "endpush",
2146
- "pushif",
2147
- "endpushif",
2148
- "pushonce",
2149
- "endpushonce",
2150
- "prepend",
2151
- "endprepend",
2152
- "prependonce",
2153
- "endprependonce",
2154
- "include",
2155
- "includeif",
2156
- "includewhen",
2157
- "includeunless",
2158
- "includefirst",
2159
- "auth",
2160
- "endauth",
2161
- "guest",
2162
- "endguest",
2163
- "can",
2164
- "endcan",
2165
- "cannot",
2166
- "endcannot",
2167
- "elsecan",
2168
- "env",
2169
- "endenv",
2170
- "isset",
2171
- "endisset",
2172
- "empty",
2173
- "endempty",
2174
- "error",
2175
- "enderror",
2176
- "hassection",
2177
- "once",
2178
- "endonce",
2179
- "use",
2180
- "csrf",
2181
- "method",
2182
- "locale",
2183
- "transition",
2184
- "endtransition",
2185
- "motion",
2186
- "endmotion",
2187
- "animationgroup",
2188
- "scrollanimate",
2189
- "endscrollanimate"
2190
- ];
2191
- if (!validDirectives.includes(directiveName.toLowerCase())) {
2399
+ const directiveDefinition = STX_DIRECTIVE_BY_NAME.get(directiveName.toLowerCase());
2400
+ if (!directiveDefinition) {
2192
2401
  return null;
2193
2402
  }
2194
2403
  const hover = new vscode2.MarkdownString;
@@ -3765,8 +3974,13 @@ errors.notFound: "Page not found"`, "yaml");
3765
3974
  }
3766
3975
  return createHoverWithDiagnostics(hover, document, position);
3767
3976
  default:
3768
- description = `stx directive: @${directiveName}`;
3977
+ description = directiveDefinition.description;
3769
3978
  hover.appendMarkdown(description);
3979
+ hover.appendMarkdown(`
3980
+
3981
+ **Syntax**
3982
+ `);
3983
+ hover.appendCodeblock(stxSnippetPreview(directiveDefinition.snippet ?? `@${directiveDefinition.name}`), "stx");
3770
3984
  return createHoverWithDiagnostics(hover, document, position);
3771
3985
  }
3772
3986
  }
@@ -3982,7 +4196,6 @@ errors.notFound: "Page not found"`, "yaml");
3982
4196
  const arrowFunctionMatch = tsContent.match(new RegExp(`const\\s+${word}\\s*=\\s*\\([^)]*\\)\\s*:[^=]+=>`, "s"));
3983
4197
  const isArrowFunction = arrowFunctionMatch !== null;
3984
4198
  const htmlTagMatch = line.match(new RegExp(`<(/)?${word}(\\s|/?>|$)`));
3985
- const commonHtmlTags = ["div", "span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "a", "img", "button", "input", "form", "label", "ul", "ol", "li", "table", "tr", "td", "th", "pre", "code", "body", "html", "head", "header", "footer", "main", "section", "article", "aside"];
3986
4199
  const isActuallyHtmlTag = htmlTagMatch !== null;
3987
4200
  const isInCodeContext = isInScriptTag(document, position) || line.includes("@ts") || line.includes("{{");
3988
4201
  const mightBeHtmlTag = isActuallyHtmlTag && !isInCodeContext;
@@ -4352,6 +4565,7 @@ function createCompletionProvider() {
4352
4565
  provideCompletionItems(document, position, token, context) {
4353
4566
  const config = vscode3.workspace.getConfiguration("stx.completion");
4354
4567
  const completionEnabled = config.get("enable", true);
4568
+ const showSnippets = config.get("showSnippets", true);
4355
4569
  if (!completionEnabled) {
4356
4570
  return;
4357
4571
  }
@@ -4363,21 +4577,26 @@ function createCompletionProvider() {
4363
4577
  console.log(`stx Completion - Processing document: ${document.fileName} (language: ${document.languageId})`);
4364
4578
  const line = document.lineAt(position.line).text;
4365
4579
  const linePrefix = line.substring(0, position.character);
4580
+ const sourceBeforeCursor = document.getText().slice(0, document.offsetAt(position));
4581
+ const attributeContext = getStxAttributeCompletionContext(sourceBeforeCursor);
4366
4582
  console.log(`stx Completion - Processing line prefix: "${linePrefix}"`);
4367
4583
  console.log(`stx Completion - Trigger character: "${context?.triggerCharacter || "none"}"`);
4584
+ if (attributeContext) {
4585
+ return getAttributeDirectiveCompletions(attributeContext.prefix, position, showSnippets);
4586
+ }
4368
4587
  if (context?.triggerCharacter === "@") {
4369
4588
  console.log(`stx Completion - Triggered by @ character, showing all directives`);
4370
- return getAllDirectiveCompletions();
4589
+ return getAllDirectiveCompletions(showSnippets);
4371
4590
  }
4372
- const directiveMatch = linePrefix.match(/@([a-z]*)$/i);
4591
+ const directiveMatch = linePrefix.match(/@([a-z:]*)$/i);
4373
4592
  if (directiveMatch) {
4374
4593
  const prefix = directiveMatch[1].toLowerCase();
4375
4594
  console.log(`stx Completion - Found directive prefix: "${prefix}"`);
4376
- return getFilteredDirectiveCompletions(prefix);
4595
+ return getFilteredDirectiveCompletions(prefix, showSnippets);
4377
4596
  }
4378
4597
  if (linePrefix.endsWith("@")) {
4379
4598
  console.log(`stx Completion - Line ends with @, showing all directives`);
4380
- return getAllDirectiveCompletions();
4599
+ return getAllDirectiveCompletions(showSnippets);
4381
4600
  }
4382
4601
  if (linePrefix.includes("@transition(")) {
4383
4602
  console.log(`stx Completion - Detected @transition parameters context`);
@@ -4411,113 +4630,31 @@ function createCompletionProvider() {
4411
4630
  }
4412
4631
  };
4413
4632
  }
4414
- function getAllDirectiveCompletions() {
4415
- return createDirectiveItems(getAllDirectives());
4633
+ function getAttributeDirectiveCompletions(prefix, position, showSnippets) {
4634
+ const normalizedPrefix = prefix.toLowerCase();
4635
+ const directives = STX_ATTRIBUTE_DIRECTIVES.filter((directive) => directive.name.toLowerCase().startsWith(normalizedPrefix));
4636
+ const start = position.translate(0, -prefix.length);
4637
+ const range = new vscode3.Range(start, position);
4638
+ return directives.map((directive) => createAttributeDirectiveItem(directive, range, showSnippets));
4416
4639
  }
4417
- function getFilteredDirectiveCompletions(prefix) {
4418
- const allDirectives = getAllDirectives();
4419
- const filteredDirectives = allDirectives.filter((d) => d.name.toLowerCase().includes(prefix.toLowerCase()));
4420
- console.log(`stx Completion - Filtered directives: ${filteredDirectives.map((d) => d.name).join(", ")}`);
4421
- return createDirectiveItems(filteredDirectives);
4640
+ function createAttributeDirectiveItem(directive, range, showSnippets) {
4641
+ const item = new vscode3.CompletionItem(directive.name, vscode3.CompletionItemKind.Property);
4642
+ item.detail = `stx ${directive.name} attribute`;
4643
+ item.documentation = new vscode3.MarkdownString(directive.description);
4644
+ item.insertText = showSnippets ? new vscode3.SnippetString(directive.snippet) : directive.name;
4645
+ item.range = range;
4646
+ item.sortText = directive.name.startsWith(":") ? `0${directive.name}` : `1${directive.name}`;
4647
+ return item;
4422
4648
  }
4423
- function getAllDirectives() {
4424
- return [
4425
- { name: "transition", description: "Applies transition animations to an element" },
4426
- { name: "motion", description: "Controls animation display based on user preferences" },
4427
- { name: "animationGroup", description: "Groups elements for coordinated animations" },
4428
- { name: "scrollAnimate", description: "Triggers animation when element scrolls into view" },
4429
- { name: "translate", description: "Translate a string using the i18n system" },
4430
- { name: "t", description: "Short alias for translate directive" },
4431
- { name: "if", description: "Conditional rendering" },
4432
- { name: "else", description: "Alternative rendering for conditionals" },
4433
- { name: "elseif", description: "Alternative condition for if statement" },
4434
- { name: "unless", description: "Inverse conditional rendering" },
4435
- { name: "foreach", description: "Iterates over an array or collection" },
4436
- { name: "for", description: "Standard for loop" },
4437
- { name: "while", description: "While loop" },
4438
- { name: "forelse", description: "Loop with fallback when collection is empty" },
4439
- { name: "switch", description: "Switch statement for multiple conditions" },
4440
- { name: "case", description: "Case option in switch statement" },
4441
- { name: "default", description: "Default case in switch statement" },
4442
- { name: "break", description: "Break from loop or switch" },
4443
- { name: "continue", description: "Continue to next iteration" },
4444
- { name: "component", description: "Renders a component" },
4445
- { name: "slot", description: "Define a named slot in a component" },
4446
- { name: "webcomponent", description: "Include a web component" },
4447
- { name: "ts", description: "TypeScript code block" },
4448
- { name: "js", description: "JavaScript code block" },
4449
- { name: "script", description: "Script code block (alternative to @ts)" },
4450
- { name: "css", description: "CSS code block" },
4451
- { name: "json", description: "Output variable as JSON" },
4452
- { name: "markdown", description: "Markdown content block" },
4453
- { name: "raw", description: "Display content without processing expressions" },
4454
- { name: "verbatim", description: "Display content without processing directives" },
4455
- { name: "extends", description: "Extend a base layout" },
4456
- { name: "section", description: "Define a content section" },
4457
- { name: "yield", description: "Output the contents of a section" },
4458
- { name: "parent", description: "Include parent section content" },
4459
- { name: "stack", description: "Define a stack for pushing content" },
4460
- { name: "push", description: "Push content to a stack" },
4461
- { name: "pushIf", description: "Conditionally push content to a stack" },
4462
- { name: "pushOnce", description: "Push content to a stack only once" },
4463
- { name: "prepend", description: "Prepend content to a stack" },
4464
- { name: "prependOnce", description: "Prepend content to a stack only once" },
4465
- { name: "include", description: "Include another template file" },
4466
- { name: "includeIf", description: "Conditionally include a template" },
4467
- { name: "includeWhen", description: "Include template when condition is true" },
4468
- { name: "includeUnless", description: "Include template unless condition is true" },
4469
- { name: "includeFirst", description: "Include the first existing template from a list" },
4470
- { name: "auth", description: "Content visible only to authenticated users" },
4471
- { name: "guest", description: "Content visible only to guests" },
4472
- { name: "can", description: "Check if user has permission" },
4473
- { name: "cannot", description: "Check if user lacks permission" },
4474
- { name: "env", description: "Check environment condition" },
4475
- { name: "isset", description: "Check if a variable is set" },
4476
- { name: "empty", description: "Check if a variable is empty" },
4477
- { name: "error", description: "Display validation error for a field" },
4478
- { name: "hasSection", description: "Check if a section exists" },
4479
- { name: "once", description: "Include content only once per rendering cycle" },
4480
- { name: "use", description: "Import a class or namespace" },
4481
- { name: "csrf", description: "Include CSRF protection token" },
4482
- { name: "method", description: "Specify form submission method" },
4483
- { name: "locale", description: "Set locale for content" },
4484
- { name: "endtransition", description: "Ends a transition block" },
4485
- { name: "endmotion", description: "Ends a motion block" },
4486
- { name: "endif", description: "Ends an if block" },
4487
- { name: "endunless", description: "Ends an unless block" },
4488
- { name: "endfor", description: "Ends a for loop block" },
4489
- { name: "endforeach", description: "Ends a foreach loop block" },
4490
- { name: "endforelse", description: "Ends a forelse loop block" },
4491
- { name: "endwhile", description: "Ends a while loop block" },
4492
- { name: "endswitch", description: "Ends a switch block" },
4493
- { name: "endts", description: "Ends a TypeScript code block" },
4494
- { name: "endjs", description: "Ends a JavaScript code block" },
4495
- { name: "endscript", description: "Ends a script code block" },
4496
- { name: "endcss", description: "Ends a CSS code block" },
4497
- { name: "endmarkdown", description: "Ends a markdown block" },
4498
- { name: "endraw", description: "Ends a raw content block" },
4499
- { name: "endverbatim", description: "Ends a verbatim block" },
4500
- { name: "endcomponent", description: "Ends a component block" },
4501
- { name: "endslot", description: "Ends a slot block" },
4502
- { name: "endsection", description: "Ends a section block" },
4503
- { name: "endpush", description: "Ends a push block" },
4504
- { name: "endpushIf", description: "Ends a pushIf block" },
4505
- { name: "endpushOnce", description: "Ends a pushOnce block" },
4506
- { name: "endprepend", description: "Ends a prepend block" },
4507
- { name: "endprependOnce", description: "Ends a prependOnce block" },
4508
- { name: "endauth", description: "Ends an auth block" },
4509
- { name: "endguest", description: "Ends a guest block" },
4510
- { name: "endcan", description: "Ends a can block" },
4511
- { name: "endcannot", description: "Ends a cannot block" },
4512
- { name: "endempty", description: "Ends an empty block" },
4513
- { name: "endenv", description: "Ends an env block" },
4514
- { name: "endisset", description: "Ends an isset block" },
4515
- { name: "enderror", description: "Ends an error block" },
4516
- { name: "endonce", description: "Ends a once block" },
4517
- { name: "endwebcomponent", description: "Ends a webcomponent block" }
4518
- ];
4649
+ function getAllDirectiveCompletions(showSnippets) {
4650
+ return createDirectiveItems(STX_DIRECTIVES, showSnippets);
4651
+ }
4652
+ function getFilteredDirectiveCompletions(prefix, showSnippets) {
4653
+ const filteredDirectives = STX_DIRECTIVES.filter((d) => d.name.toLowerCase().includes(prefix.toLowerCase()));
4654
+ console.log(`stx Completion - Filtered directives: ${filteredDirectives.map((d) => d.name).join(", ")}`);
4655
+ return createDirectiveItems(filteredDirectives, showSnippets);
4519
4656
  }
4520
- function createDirectiveItems(directives) {
4657
+ function createDirectiveItems(directives, showSnippets) {
4521
4658
  return directives.map((directive) => {
4522
4659
  const item = new vscode3.CompletionItem(`@${directive.name}`, vscode3.CompletionItemKind.Snippet);
4523
4660
  item.detail = `stx ${directive.name} directive`;
@@ -4712,9 +4849,11 @@ function createDirectiveItems(directives) {
4712
4849
  item.insertText = new vscode3.SnippetString("@animationGroup(${1:groupName}, ${2:#selector1})");
4713
4850
  break;
4714
4851
  default:
4715
- item.insertText = new vscode3.SnippetString(`@${directive.name}`);
4852
+ item.insertText = new vscode3.SnippetString(directive.snippet ?? `@${directive.name}`);
4716
4853
  break;
4717
4854
  }
4855
+ if (!showSnippets)
4856
+ item.insertText = `@${directive.name}`;
4718
4857
  if (["transition", "motion", "translate", "component", "if", "foreach"].includes(directive.name)) {
4719
4858
  item.sortText = `0${directive.name}`;
4720
4859
  } else {
@@ -4861,20 +5000,122 @@ function createPropsCompletions(document, partialProp) {
4861
5000
  // ../vscode/src/providers/definitionProvider.ts
4862
5001
  import * as vscode5 from "vscode";
4863
5002
 
5003
+ // ../vscode/src/utils/template-paths.ts
5004
+ var TEMPLATE_SEARCH_DIRECTORIES = [
5005
+ "",
5006
+ "views",
5007
+ "templates",
5008
+ "resources/views",
5009
+ "resources/templates",
5010
+ "src/views",
5011
+ "src/templates",
5012
+ "app/views",
5013
+ "partials",
5014
+ "components",
5015
+ "layouts",
5016
+ "examples/partials",
5017
+ "examples/components"
5018
+ ];
5019
+ var COMPONENT_SEARCH_DIRECTORIES = [
5020
+ "components",
5021
+ "resources/views/components",
5022
+ "src/components",
5023
+ "app/components",
5024
+ "ui",
5025
+ "src/ui"
5026
+ ];
5027
+ var TEMPLATE_DIRECTIVE_PATTERN = /@(includeFirst|includeUnless|includeWhen|includeIf|include|webcomponent|component)\s*\(/g;
5028
+ function argumentStart(source, openParen, targetArgument) {
5029
+ if (targetArgument === 0)
5030
+ return openParen + 1;
5031
+ let argument = 0;
5032
+ let depth = 0;
5033
+ let quote = "";
5034
+ for (let index = openParen + 1;index < source.length; index++) {
5035
+ const character = source[index];
5036
+ if (quote) {
5037
+ if (character === "\\")
5038
+ index++;
5039
+ else if (character === quote)
5040
+ quote = "";
5041
+ continue;
5042
+ }
5043
+ if (character === "'" || character === '"' || character === "`") {
5044
+ quote = character;
5045
+ } else if (character === "(" || character === "[" || character === "{") {
5046
+ depth++;
5047
+ } else if (character === ")" || character === "]" || character === "}") {
5048
+ if (character === ")" && depth === 0)
5049
+ return;
5050
+ depth--;
5051
+ } else if (character === "," && depth === 0) {
5052
+ argument++;
5053
+ if (argument === targetArgument)
5054
+ return index + 1;
5055
+ }
5056
+ }
5057
+ return;
5058
+ }
5059
+ function quotedReferences(source, start, end = source.length) {
5060
+ const references = [];
5061
+ for (let index = start;index < end; index++) {
5062
+ const quote = source[index];
5063
+ if (quote !== "'" && quote !== '"' && quote !== "`")
5064
+ continue;
5065
+ const pathStart = index + 1;
5066
+ let pathEnd = pathStart;
5067
+ while (pathEnd < end && source[pathEnd] !== quote) {
5068
+ if (source[pathEnd] === "\\")
5069
+ pathEnd++;
5070
+ pathEnd++;
5071
+ }
5072
+ if (pathEnd < end) {
5073
+ references.push({ path: source.slice(pathStart, pathEnd), start: pathStart, end: pathEnd });
5074
+ index = pathEnd;
5075
+ }
5076
+ }
5077
+ return references;
5078
+ }
5079
+ function getTemplatePathCompletionContext(linePrefix) {
5080
+ TEMPLATE_DIRECTIVE_PATTERN.lastIndex = 0;
5081
+ const directives = [...linePrefix.matchAll(TEMPLATE_DIRECTIVE_PATTERN)];
5082
+ const directive = directives[directives.length - 1];
5083
+ if (!directive)
5084
+ return;
5085
+ const name = directive[1];
5086
+ const openParen = directive.index + directive[0].lastIndexOf("(");
5087
+ const targetArgument = name === "includeWhen" || name === "includeUnless" ? 1 : 0;
5088
+ const start = argumentStart(linePrefix, openParen, targetArgument);
5089
+ if (start === undefined)
5090
+ return;
5091
+ const argument = linePrefix.slice(start);
5092
+ const pathMatch = name === "includeFirst" ? argument.match(/\[[\s\S]*['"`]([^'"`]*)$/) : argument.match(/^\s*['"`]([^'"`]*)$/);
5093
+ return pathMatch ? { kind: name === "component" || name === "webcomponent" ? "component" : "include", path: pathMatch[1] } : undefined;
5094
+ }
5095
+ function findTemplatePathReferences(line) {
5096
+ const references = [];
5097
+ TEMPLATE_DIRECTIVE_PATTERN.lastIndex = 0;
5098
+ for (const directive of line.matchAll(TEMPLATE_DIRECTIVE_PATTERN)) {
5099
+ const name = directive[1];
5100
+ const openParen = directive.index + directive[0].lastIndexOf("(");
5101
+ const targetArgument = name === "includeWhen" || name === "includeUnless" ? 1 : 0;
5102
+ const start = argumentStart(line, openParen, targetArgument);
5103
+ if (start === undefined)
5104
+ continue;
5105
+ const kind = name === "component" || name === "webcomponent" ? "component" : "include";
5106
+ const candidates = name === "includeFirst" ? quotedReferences(line, line.indexOf("[", start) + 1, line.indexOf("]", start)) : quotedReferences(line, start).slice(0, 1);
5107
+ for (const candidate of candidates)
5108
+ references.push({ kind, ...candidate });
5109
+ }
5110
+ return references.sort((a, b) => a.start - b.start);
5111
+ }
5112
+
4864
5113
  // ../vscode/src/utils/templateUtils.ts
4865
5114
  import * as fs2 from "node:fs";
4866
5115
  import * as path3 from "node:path";
4867
5116
  import * as vscode4 from "vscode";
4868
5117
  function extractTemplatePath(line) {
4869
- const regularMatch = line.match(/@(?:include|component)\s*\(\s*['"]([^'"]+)['"]/);
4870
- if (regularMatch && regularMatch[1]) {
4871
- return regularMatch[1];
4872
- }
4873
- const conditionalMatch = line.match(/@(?:includeIf|includeWhen|includeUnless|includeFirst)\s*\([^,]*,\s*['"]([^'"]+)['"]/);
4874
- if (conditionalMatch && conditionalMatch[1]) {
4875
- return conditionalMatch[1];
4876
- }
4877
- return null;
5118
+ return findTemplatePathReferences(line)[0]?.path ?? null;
4878
5119
  }
4879
5120
  function normalizeTemplatePath(templatePath) {
4880
5121
  let normalizedPath = templatePath.replace(/\./g, "/");
@@ -4894,18 +5135,11 @@ function resolveTemplatePath(documentUri, templatePath) {
4894
5135
  if (fs2.existsSync(relativePath)) {
4895
5136
  return vscode4.Uri.file(relativePath);
4896
5137
  }
4897
- const searchDirs = [
4898
- "",
4899
- "views",
4900
- "templates",
4901
- "partials",
4902
- "components",
4903
- "resources/views",
4904
- "resources/templates",
4905
- "examples/partials",
4906
- "examples/components"
4907
- ];
4908
- for (const dir of searchDirs) {
5138
+ const searchDirectories = [...new Set([
5139
+ ...TEMPLATE_SEARCH_DIRECTORIES,
5140
+ ...COMPONENT_SEARCH_DIRECTORIES
5141
+ ])];
5142
+ for (const dir of searchDirectories) {
4909
5143
  const fullPath = path3.join(workspaceFolder.uri.fsPath, dir, normalizedPath);
4910
5144
  if (fs2.existsSync(fullPath)) {
4911
5145
  return vscode4.Uri.file(fullPath);
@@ -4915,45 +5149,27 @@ function resolveTemplatePath(documentUri, templatePath) {
4915
5149
  }
4916
5150
  function getTemplatePathRange(document, line) {
4917
5151
  const lineText = document.lineAt(line).text;
4918
- const regularMatch = lineText.match(/@(?:include|component)\s*\(\s*(['"])([^'"]+)\1/);
4919
- if (regularMatch) {
4920
- const startPos = lineText.indexOf(regularMatch[2], regularMatch.index);
4921
- const endPos = startPos + regularMatch[2].length;
4922
- return new vscode4.Range(new vscode4.Position(line, startPos), new vscode4.Position(line, endPos));
4923
- }
4924
- const conditionalMatch = lineText.match(/@(?:includeIf|includeWhen|includeUnless|includeFirst)\s*\([^,]*,\s*(['"])([^'"]+)\1/);
4925
- if (conditionalMatch) {
4926
- const startPos = lineText.indexOf(conditionalMatch[2], conditionalMatch.index);
4927
- const endPos = startPos + conditionalMatch[2].length;
4928
- return new vscode4.Range(new vscode4.Position(line, startPos), new vscode4.Position(line, endPos));
4929
- }
4930
- return null;
5152
+ const reference = findTemplatePathReferences(lineText)[0];
5153
+ return reference ? new vscode4.Range(line, reference.start, line, reference.end) : null;
4931
5154
  }
4932
5155
 
4933
5156
  // ../vscode/src/providers/definitionProvider.ts
4934
5157
  function createDefinitionProvider(virtualTsDocumentProvider) {
4935
5158
  return {
4936
5159
  async provideDefinition(document, position, token) {
5160
+ const line = document.lineAt(position.line).text;
5161
+ const templateReference = findTemplatePathReferences(line).find((reference) => position.character >= reference.start && position.character <= reference.end);
5162
+ if (templateReference) {
5163
+ const resolvedUri = resolveTemplatePath(document.uri, templateReference.path);
5164
+ if (resolvedUri) {
5165
+ return new vscode5.Location(resolvedUri, new vscode5.Position(0, 0));
5166
+ }
5167
+ }
4937
5168
  const wordRange = document.getWordRangeAtPosition(position);
4938
5169
  if (!wordRange) {
4939
5170
  return null;
4940
5171
  }
4941
5172
  const word = document.getText(wordRange);
4942
- const line = document.lineAt(position.line).text;
4943
- const isRegularDirective = /@(include|component)\s*\(/.test(line);
4944
- const isConditionalDirective = /@(includeIf|includeWhen|includeUnless|includeFirst)\s*\([^,]*,/.test(line);
4945
- if (isRegularDirective || isConditionalDirective) {
4946
- const templatePath = extractTemplatePath(line);
4947
- if (templatePath) {
4948
- const templatePathRange = getTemplatePathRange(document, position.line);
4949
- if (templatePathRange && templatePathRange.contains(position)) {
4950
- const resolvedUri = resolveTemplatePath(document.uri, templatePath);
4951
- if (resolvedUri) {
4952
- return new vscode5.Location(resolvedUri, new vscode5.Position(0, 0));
4953
- }
4954
- }
4955
- }
4956
- }
4957
5173
  const classAttributeRegex = /(class|className)\s*=\s*["']([^"']*)["']/g;
4958
5174
  let classAttributeMatch;
4959
5175
  let isInClassAttribute = false;
@@ -5038,43 +5254,6 @@ function createDiagnosticsProvider(context) {
5038
5254
  }));
5039
5255
  return diagnosticCollection;
5040
5256
  }
5041
- var directivePairs = {
5042
- if: "endif",
5043
- unless: "endunless",
5044
- for: "endfor",
5045
- foreach: "endforeach",
5046
- forelse: "endforelse",
5047
- while: "endwhile",
5048
- switch: "endswitch",
5049
- ts: "endts",
5050
- js: "endjs",
5051
- script: "endscript",
5052
- css: "endcss",
5053
- markdown: "endmarkdown",
5054
- raw: "endraw",
5055
- verbatim: "endverbatim",
5056
- component: "endcomponent",
5057
- slot: "endslot",
5058
- webcomponent: "endwebcomponent",
5059
- section: "endsection",
5060
- push: "endpush",
5061
- pushIf: "endpushIf",
5062
- pushOnce: "endpushOnce",
5063
- prepend: "endprepend",
5064
- prependOnce: "endprependOnce",
5065
- auth: "endauth",
5066
- guest: "endguest",
5067
- can: "endcan",
5068
- cannot: "endcannot",
5069
- env: "endenv",
5070
- isset: "endisset",
5071
- empty: "endempty",
5072
- error: "enderror",
5073
- once: "endonce",
5074
- transition: "endtransition",
5075
- motion: "endmotion",
5076
- scrollAnimate: "endscrollAnimate"
5077
- };
5078
5257
  function updateDiagnostics(document, diagnosticCollection) {
5079
5258
  const config = vscode6.workspace.getConfiguration("stx.diagnostics");
5080
5259
  const diagnosticsEnabled = config.get("enable", true);
@@ -5098,7 +5277,7 @@ function updateDiagnostics(document, diagnosticCollection) {
5098
5277
  while (match !== null) {
5099
5278
  const directiveName = match[1];
5100
5279
  const position = match.index + 1;
5101
- if (directivePairs[directiveName]) {
5280
+ if (STX_DIRECTIVE_PAIRS[directiveName]) {
5102
5281
  stack.push({
5103
5282
  name: directiveName,
5104
5283
  line: lineNum,
@@ -5107,14 +5286,14 @@ function updateDiagnostics(document, diagnosticCollection) {
5107
5286
  } else {
5108
5287
  const isEndDirective = directiveName.startsWith("end");
5109
5288
  if (isEndDirective) {
5110
- const openingName = Object.keys(directivePairs).find((key) => directivePairs[key] === directiveName);
5289
+ const openingName = STX_OPENING_DIRECTIVE_BY_CLOSE.get(directiveName);
5111
5290
  if (openingName && validateMismatched) {
5112
5291
  if (stack.length > 0) {
5113
5292
  const lastOpening = stack[stack.length - 1];
5114
5293
  if (lastOpening.name === openingName) {
5115
5294
  stack.pop();
5116
5295
  } else {
5117
- const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(lineNum, position), new vscode6.Position(lineNum, position + directiveName.length)), `Mismatched directive. Expected @${directivePairs[lastOpening.name]} but found @${directiveName}`, vscode6.DiagnosticSeverity.Error);
5296
+ const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(lineNum, position), new vscode6.Position(lineNum, position + directiveName.length)), `Mismatched directive. Expected @${STX_DIRECTIVE_PAIRS[lastOpening.name]} but found @${directiveName}`, vscode6.DiagnosticSeverity.Error);
5118
5297
  diagnostic.source = "stx";
5119
5298
  diagnostics.push(diagnostic);
5120
5299
  }
@@ -5135,7 +5314,7 @@ function updateDiagnostics(document, diagnosticCollection) {
5135
5314
  }
5136
5315
  if (validateUnclosed) {
5137
5316
  for (const unclosed of stack) {
5138
- const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(unclosed.line, unclosed.character), new vscode6.Position(unclosed.line, unclosed.character + unclosed.name.length)), `Unclosed @${unclosed.name} directive. Expected @${directivePairs[unclosed.name]}`, vscode6.DiagnosticSeverity.Warning);
5317
+ const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(unclosed.line, unclosed.character), new vscode6.Position(unclosed.line, unclosed.character + unclosed.name.length)), `Unclosed @${unclosed.name} directive. Expected @${STX_DIRECTIVE_PAIRS[unclosed.name]}`, vscode6.DiagnosticSeverity.Warning);
5139
5318
  diagnostic.source = "stx";
5140
5319
  diagnostics.push(diagnostic);
5141
5320
  }
@@ -5149,38 +5328,22 @@ function updateDiagnostics(document, diagnosticCollection) {
5149
5328
  diagnosticCollection.set(document.uri, diagnostics);
5150
5329
  }
5151
5330
  function validateTemplatePaths(document, diagnostics) {
5152
- const workspaceFolder = vscode6.workspace.workspaceFolders?.[0];
5331
+ const workspaceFolder = vscode6.workspace.getWorkspaceFolder(document.uri);
5153
5332
  if (!workspaceFolder) {
5154
5333
  return;
5155
5334
  }
5156
5335
  const workspacePath = workspaceFolder.uri.fsPath;
5157
- const templateDirs = [
5158
- "views",
5159
- "templates",
5160
- "resources/views",
5161
- "src/views",
5162
- "src/templates",
5163
- "app/views",
5164
- "partials",
5165
- "components",
5166
- "layouts",
5167
- "src/components",
5168
- "app/components"
5169
- ];
5170
- const pathDirectiveRegex = /@(include(?:If|When|Unless|First)?|component|webcomponent)\s*\(\s*['"`]([^'"`]+)['"`]/g;
5336
+ const templateDirs = [...new Set([
5337
+ ...TEMPLATE_SEARCH_DIRECTORIES,
5338
+ ...COMPONENT_SEARCH_DIRECTORIES
5339
+ ])];
5171
5340
  for (let lineNum = 0;lineNum < document.lineCount; lineNum++) {
5172
5341
  const line = document.lineAt(lineNum);
5173
5342
  const text = line.text;
5174
- let match;
5175
- pathDirectiveRegex.lastIndex = 0;
5176
- match = pathDirectiveRegex.exec(text);
5177
- while (match !== null) {
5178
- const directiveName = match[1];
5179
- const templatePath = match[2];
5180
- const pathStart = match.index + match[0].indexOf(templatePath);
5181
- if (directiveName === "includeFirst") {
5182
- continue;
5183
- }
5343
+ if (/@includeFirst\s*\(/.test(text))
5344
+ continue;
5345
+ for (const reference of findTemplatePathReferences(text)) {
5346
+ const templatePath = reference.path;
5184
5347
  let found = false;
5185
5348
  for (const templateDir of templateDirs) {
5186
5349
  const stxPath = path4.join(workspacePath, templateDir, `${templatePath}.stx`);
@@ -5200,12 +5363,11 @@ function validateTemplatePaths(document, diagnostics) {
5200
5363
  }
5201
5364
  }
5202
5365
  if (!found) {
5203
- const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(lineNum, pathStart), new vscode6.Position(lineNum, pathStart + templatePath.length)), `Template file '${templatePath}' not found in any template directory`, vscode6.DiagnosticSeverity.Warning);
5366
+ const diagnostic = new vscode6.Diagnostic(new vscode6.Range(new vscode6.Position(lineNum, reference.start), new vscode6.Position(lineNum, reference.end)), `Template file '${templatePath}' not found in any template directory`, vscode6.DiagnosticSeverity.Warning);
5204
5367
  diagnostic.code = "template-not-found";
5205
5368
  diagnostic.source = "stx";
5206
5369
  diagnostics.push(diagnostic);
5207
5370
  }
5208
- match = pathDirectiveRegex.exec(text);
5209
5371
  }
5210
5372
  }
5211
5373
  }
@@ -5270,20 +5432,13 @@ function createDocumentLinkProvider() {
5270
5432
  const links = [];
5271
5433
  for (let lineIndex = 0;lineIndex < document.lineCount; lineIndex++) {
5272
5434
  const line = document.lineAt(lineIndex).text;
5273
- const isRegularDirective = /@(include|component)\s*\(/.test(line);
5274
- const isConditionalDirective = /@(includeIf|includeWhen|includeUnless|includeFirst)\s*\([^,]*,/.test(line);
5275
- if (isRegularDirective || isConditionalDirective) {
5276
- const templatePath = extractTemplatePath(line);
5277
- if (templatePath) {
5278
- const range = getTemplatePathRange(document, lineIndex);
5279
- if (range) {
5280
- const resolvedUri = resolveTemplatePath(document.uri, templatePath);
5281
- if (resolvedUri) {
5282
- const documentLink = new vscode7.DocumentLink(range, resolvedUri);
5283
- documentLink.tooltip = `Open ${templatePath}`;
5284
- links.push(documentLink);
5285
- }
5286
- }
5435
+ for (const reference of findTemplatePathReferences(line)) {
5436
+ const resolvedUri = resolveTemplatePath(document.uri, reference.path);
5437
+ if (resolvedUri) {
5438
+ const range = new vscode7.Range(lineIndex, reference.start, lineIndex, reference.end);
5439
+ const documentLink = new vscode7.DocumentLink(range, resolvedUri);
5440
+ documentLink.tooltip = `Open ${reference.path}`;
5441
+ links.push(documentLink);
5287
5442
  }
5288
5443
  }
5289
5444
  }
@@ -5300,32 +5455,21 @@ function createPathCompletionProvider() {
5300
5455
  provideCompletionItems(document, position, _token, _context) {
5301
5456
  const line = document.lineAt(position.line).text;
5302
5457
  const linePrefix = line.substring(0, position.character);
5303
- const includeMatch = linePrefix.match(/@include(?:If|When|Unless|First)?\s*\(\s*['"`]([^'"`]*)$/);
5304
- const componentMatch = linePrefix.match(/@(?:component|webcomponent)\s*\(\s*['"`]([^'"`]*)$/);
5305
- if (!includeMatch && !componentMatch) {
5458
+ const pathContext = getTemplatePathCompletionContext(linePrefix);
5459
+ if (!pathContext) {
5306
5460
  return;
5307
5461
  }
5308
- const currentPath = includeMatch ? includeMatch[1] : componentMatch[1];
5309
- const isInclude = !!includeMatch;
5310
- const workspaceFolder = vscode8.workspace.workspaceFolders?.[0];
5462
+ const currentPath = pathContext.path;
5463
+ const isInclude = pathContext.kind === "include";
5464
+ const workspaceFolder = vscode8.workspace.getWorkspaceFolder(document.uri);
5311
5465
  if (!workspaceFolder) {
5312
5466
  return;
5313
5467
  }
5314
5468
  const workspacePath = workspaceFolder.uri.fsPath;
5315
- const templateDirs = [
5316
- "views",
5317
- "templates",
5318
- "resources/views",
5319
- "src/views",
5320
- "src/templates",
5321
- "app/views",
5322
- "partials",
5323
- "components",
5324
- "layouts"
5325
- ];
5326
- if (!isInclude) {
5327
- templateDirs.push("src/components", "app/components", "ui", "src/ui");
5328
- }
5469
+ const templateDirs = [...new Set([
5470
+ ...TEMPLATE_SEARCH_DIRECTORIES.filter(Boolean),
5471
+ ...!isInclude ? COMPONENT_SEARCH_DIRECTORIES : []
5472
+ ])];
5329
5473
  const completions = [];
5330
5474
  let searchPath = workspacePath;
5331
5475
  let searchRelative = "";
@@ -5402,6 +5546,22 @@ function createPathCompletionProvider() {
5402
5546
  }
5403
5547
  // ../vscode/src/providers/codeActionsProvider.ts
5404
5548
  import * as vscode9 from "vscode";
5549
+
5550
+ // ../vscode/src/utils/directive-code-actions.ts
5551
+ function directiveDiagnosticAction(message) {
5552
+ const unclosed = message.match(/^Unclosed @\w+ directive\. Expected @(\w+)$/);
5553
+ if (unclosed)
5554
+ return { kind: "add-closing", directive: unclosed[1] };
5555
+ const mismatched = message.match(/^Mismatched directive\. Expected @(\w+) but found @\w+$/);
5556
+ if (mismatched)
5557
+ return { kind: "replace-closing", directive: mismatched[1] };
5558
+ const unexpected = message.match(/^Unexpected @(\w+)\. No matching opening directive found$/);
5559
+ if (unexpected)
5560
+ return { kind: "remove-closing", directive: unexpected[1] };
5561
+ return;
5562
+ }
5563
+
5564
+ // ../vscode/src/providers/codeActionsProvider.ts
5405
5565
  function createCodeActionsProvider() {
5406
5566
  return {
5407
5567
  provideCodeActions(document, range, context, _token) {
@@ -5411,92 +5571,44 @@ function createCodeActionsProvider() {
5411
5571
  return [];
5412
5572
  }
5413
5573
  const actions = [];
5414
- const _directivePairs = {
5415
- if: "endif",
5416
- unless: "endunless",
5417
- for: "endfor",
5418
- foreach: "endforeach",
5419
- forelse: "endforelse",
5420
- while: "endwhile",
5421
- switch: "endswitch",
5422
- ts: "endts",
5423
- js: "endjs",
5424
- script: "endscript",
5425
- css: "endcss",
5426
- markdown: "endmarkdown",
5427
- raw: "endraw",
5428
- verbatim: "endverbatim",
5429
- component: "endcomponent",
5430
- slot: "endslot",
5431
- webcomponent: "endwebcomponent",
5432
- section: "endsection",
5433
- push: "endpush",
5434
- pushIf: "endpushIf",
5435
- pushOnce: "endpushOnce",
5436
- prepend: "endprepend",
5437
- prependOnce: "endprependOnce",
5438
- auth: "endauth",
5439
- guest: "endguest",
5440
- can: "endcan",
5441
- cannot: "endcannot",
5442
- env: "endenv",
5443
- isset: "endisset",
5444
- empty: "endempty",
5445
- error: "enderror",
5446
- once: "endonce",
5447
- transition: "endtransition",
5448
- motion: "endmotion",
5449
- scrollAnimate: "endscrollAnimate"
5450
- };
5451
5574
  for (const diagnostic of context.diagnostics) {
5452
- if (diagnostic.message.includes("Unclosed @")) {
5453
- const match = diagnostic.message.match(/Unclosed @(\w).*Expected @(\w+)/);
5454
- if (match) {
5455
- const _directiveName = match[1];
5456
- const endDirective = match[2];
5457
- const fix = new vscode9.CodeAction(`Add missing @${endDirective}`, vscode9.CodeActionKind.QuickFix);
5458
- fix.diagnostics = [diagnostic];
5459
- fix.isPreferred = true;
5460
- let insertLine = document.lineCount - 1;
5461
- for (let i = diagnostic.range.start.line + 1;i < document.lineCount; i++) {
5462
- const line = document.lineAt(i).text.trim();
5463
- if (line.match(/^@\w+/) && !line.startsWith(`@end`)) {
5464
- insertLine = i - 1;
5465
- break;
5466
- }
5575
+ const diagnosticAction = directiveDiagnosticAction(diagnostic.message);
5576
+ if (diagnosticAction?.kind === "add-closing") {
5577
+ const endDirective = diagnosticAction.directive;
5578
+ const fix = new vscode9.CodeAction(`Add missing @${endDirective}`, vscode9.CodeActionKind.QuickFix);
5579
+ fix.diagnostics = [diagnostic];
5580
+ fix.isPreferred = true;
5581
+ let insertLine = document.lineCount - 1;
5582
+ for (let i = diagnostic.range.start.line + 1;i < document.lineCount; i++) {
5583
+ const line = document.lineAt(i).text.trim();
5584
+ if (line.match(/^@\w+/) && !line.startsWith(`@end`)) {
5585
+ insertLine = i - 1;
5586
+ break;
5467
5587
  }
5468
- const openingLine = document.lineAt(diagnostic.range.start.line);
5469
- const indent = openingLine.text.match(/^\s*/)?.[0] || "";
5470
- fix.edit = new vscode9.WorkspaceEdit;
5471
- fix.edit.insert(document.uri, new vscode9.Position(insertLine + 1, 0), `${indent}@${endDirective}
5472
- `);
5473
- actions.push(fix);
5474
5588
  }
5589
+ const openingLine = document.lineAt(diagnostic.range.start.line);
5590
+ const indent = openingLine.text.match(/^\s*/)?.[0] || "";
5591
+ fix.edit = new vscode9.WorkspaceEdit;
5592
+ fix.edit.insert(document.uri, new vscode9.Position(insertLine + 1, 0), `${indent}@${endDirective}
5593
+ `);
5594
+ actions.push(fix);
5475
5595
  }
5476
- if (diagnostic.message.includes("Mismatched directive")) {
5477
- const match = diagnostic.message.match(/Expected @(\w+) but found @(\w+)/);
5478
- if (match) {
5479
- const expectedEnd = match[1];
5480
- const _foundEnd = match[2];
5481
- const fix = new vscode9.CodeAction(`Change to @${expectedEnd}`, vscode9.CodeActionKind.QuickFix);
5482
- fix.diagnostics = [diagnostic];
5483
- fix.isPreferred = true;
5484
- fix.edit = new vscode9.WorkspaceEdit;
5485
- fix.edit.replace(document.uri, diagnostic.range, expectedEnd);
5486
- actions.push(fix);
5487
- }
5596
+ if (diagnosticAction?.kind === "replace-closing") {
5597
+ const expectedEnd = diagnosticAction.directive;
5598
+ const fix = new vscode9.CodeAction(`Change to @${expectedEnd}`, vscode9.CodeActionKind.QuickFix);
5599
+ fix.diagnostics = [diagnostic];
5600
+ fix.isPreferred = true;
5601
+ fix.edit = new vscode9.WorkspaceEdit;
5602
+ fix.edit.replace(document.uri, diagnostic.range, expectedEnd);
5603
+ actions.push(fix);
5488
5604
  }
5489
- if (diagnostic.message.includes("Unexpected @end")) {
5490
- const match = diagnostic.message.match(/Unexpected @(\w+)/);
5491
- if (match) {
5492
- const directiveName = match[1];
5493
- const removeFix = new vscode9.CodeAction(`Remove @${directiveName}`, vscode9.CodeActionKind.QuickFix);
5494
- removeFix.diagnostics = [diagnostic];
5495
- const line = document.lineAt(diagnostic.range.start.line);
5496
- removeFix.edit = new vscode9.WorkspaceEdit;
5497
- removeFix.edit.delete(document.uri, line.range);
5498
- actions.push(removeFix);
5499
- }
5605
+ if (diagnosticAction?.kind === "remove-closing") {
5606
+ const directiveName = diagnosticAction.directive;
5607
+ const removeFix = new vscode9.CodeAction(`Remove @${directiveName}`, vscode9.CodeActionKind.QuickFix);
5608
+ removeFix.diagnostics = [diagnostic];
5609
+ removeFix.edit = new vscode9.WorkspaceEdit;
5610
+ removeFix.edit.delete(document.uri, diagnostic.range.with(diagnostic.range.start.translate(0, -1), diagnostic.range.end));
5611
+ actions.push(removeFix);
5500
5612
  }
5501
5613
  }
5502
5614
  const line = document.lineAt(range.start.line);
@@ -5544,43 +5656,6 @@ function createFoldingRangeProvider() {
5544
5656
  return [];
5545
5657
  }
5546
5658
  const foldingRanges = [];
5547
- const directivePairs = {
5548
- if: "endif",
5549
- unless: "endunless",
5550
- for: "endfor",
5551
- foreach: "endforeach",
5552
- forelse: "endforelse",
5553
- while: "endwhile",
5554
- switch: "endswitch",
5555
- ts: "endts",
5556
- js: "endjs",
5557
- script: "endscript",
5558
- css: "endcss",
5559
- markdown: "endmarkdown",
5560
- raw: "endraw",
5561
- verbatim: "endverbatim",
5562
- component: "endcomponent",
5563
- slot: "endslot",
5564
- webcomponent: "endwebcomponent",
5565
- section: "endsection",
5566
- push: "endpush",
5567
- pushIf: "endpushIf",
5568
- pushOnce: "endpushOnce",
5569
- prepend: "endprepend",
5570
- prependOnce: "endprependOnce",
5571
- auth: "endauth",
5572
- guest: "endguest",
5573
- can: "endcan",
5574
- cannot: "endcannot",
5575
- env: "endenv",
5576
- isset: "endisset",
5577
- empty: "endempty",
5578
- error: "enderror",
5579
- once: "endonce",
5580
- transition: "endtransition",
5581
- motion: "endmotion",
5582
- scrollAnimate: "endscrollAnimate"
5583
- };
5584
5659
  const stack = [];
5585
5660
  const directiveRegex = /@(\w+)(?:\(|$|\s)/g;
5586
5661
  function getFoldingKind(directiveName) {
@@ -5600,7 +5675,7 @@ function createFoldingRangeProvider() {
5600
5675
  match = directiveRegex.exec(text);
5601
5676
  while (match !== null) {
5602
5677
  const directiveName = match[1];
5603
- if (directivePairs[directiveName]) {
5678
+ if (STX_DIRECTIVE_PAIRS[directiveName]) {
5604
5679
  stack.push({
5605
5680
  name: directiveName,
5606
5681
  line: lineNum,
@@ -5609,7 +5684,7 @@ function createFoldingRangeProvider() {
5609
5684
  } else {
5610
5685
  const isEndDirective = directiveName.startsWith("end");
5611
5686
  if (isEndDirective) {
5612
- const openingName = Object.keys(directivePairs).find((key) => directivePairs[key] === directiveName);
5687
+ const openingName = STX_OPENING_DIRECTIVE_BY_CLOSE.get(directiveName);
5613
5688
  if (openingName && stack.length > 0) {
5614
5689
  const lastOpening = stack[stack.length - 1];
5615
5690
  if (lastOpening.name === openingName) {
@@ -6766,12 +6841,93 @@ function findForeachDeclarationForVariable(document, currentLine, variableName)
6766
6841
  return null;
6767
6842
  }
6768
6843
  // ../vscode/src/language.ts
6844
+ import * as path8 from "node:path";
6845
+ import * as vscode16 from "vscode";
6846
+
6847
+ // ../vscode/src/commands/extractToComponent.ts
6848
+ import * as path7 from "node:path";
6849
+ import * as vscode14 from "vscode";
6850
+
6851
+ // ../vscode/src/commands/extract-component.ts
6769
6852
  import * as path6 from "node:path";
6770
- import * as vscode15 from "vscode";
6853
+ function normalizeComponentName(input) {
6854
+ const trimmed = input.trim();
6855
+ if (!trimmed || !/^[A-Za-z][A-Za-z0-9 _-]*$/.test(trimmed))
6856
+ return;
6857
+ if (/^[A-Z][A-Za-z0-9]*$/.test(trimmed))
6858
+ return trimmed;
6859
+ const words = trimmed.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean);
6860
+ return words.map((word) => word[0].toUpperCase() + word.slice(1)).join("");
6861
+ }
6862
+ function extractedComponentContent(selection) {
6863
+ const lines = selection.replace(/\r\n/g, `
6864
+ `).split(`
6865
+ `);
6866
+ while (lines[0]?.trim() === "")
6867
+ lines.shift();
6868
+ while (lines[lines.length - 1]?.trim() === "")
6869
+ lines.pop();
6870
+ const indents = lines.filter((line) => line.trim()).map((line) => line.match(/^\s*/)?.[0].length ?? 0);
6871
+ const commonIndent = indents.length ? Math.min(...indents) : 0;
6872
+ return `${lines.map((line) => line.slice(commonIndent)).join(`
6873
+ `)}
6874
+ `;
6875
+ }
6876
+ function componentDirectoryForDocument(workspacePath, documentPath) {
6877
+ const relative2 = path6.relative(workspacePath, documentPath);
6878
+ const segments = relative2.split(path6.sep);
6879
+ if (segments[0] === "resources" && segments[1] === "views")
6880
+ return path6.join(workspacePath, "resources", "views", "components");
6881
+ if (segments[0] === "src")
6882
+ return path6.join(workspacePath, "src", "components");
6883
+ return path6.join(workspacePath, "components");
6884
+ }
6885
+
6886
+ // ../vscode/src/commands/extractToComponent.ts
6887
+ function registerExtractToComponentCommand() {
6888
+ return vscode14.commands.registerCommand("stx.extractToComponent", async (document, range) => {
6889
+ const selection = document.getText(range);
6890
+ if (!selection.trim())
6891
+ return;
6892
+ const requestedName = await vscode14.window.showInputBox({
6893
+ prompt: "Name the extracted stx component",
6894
+ placeHolder: "UserCard",
6895
+ validateInput: (value) => normalizeComponentName(value) ? undefined : "Use a component name beginning with a letter"
6896
+ });
6897
+ if (!requestedName)
6898
+ return;
6899
+ const componentName = normalizeComponentName(requestedName);
6900
+ if (!componentName)
6901
+ return;
6902
+ const workspaceFolder = vscode14.workspace.getWorkspaceFolder(document.uri);
6903
+ if (!workspaceFolder) {
6904
+ await vscode14.window.showErrorMessage("Open the stx file in a workspace before extracting a component.");
6905
+ return;
6906
+ }
6907
+ const componentDirectory = componentDirectoryForDocument(workspaceFolder.uri.fsPath, document.uri.fsPath);
6908
+ const componentUri = vscode14.Uri.file(path7.join(componentDirectory, `${componentName}.stx`));
6909
+ try {
6910
+ await vscode14.workspace.fs.stat(componentUri);
6911
+ await vscode14.window.showErrorMessage(`Component ${componentName}.stx already exists.`);
6912
+ return;
6913
+ } catch {}
6914
+ await vscode14.workspace.fs.createDirectory(vscode14.Uri.file(componentDirectory));
6915
+ const edit = new vscode14.WorkspaceEdit;
6916
+ edit.createFile(componentUri);
6917
+ edit.insert(componentUri, new vscode14.Position(0, 0), extractedComponentContent(selection));
6918
+ edit.replace(document.uri, range, `<${componentName} />`);
6919
+ if (!await vscode14.workspace.applyEdit(edit)) {
6920
+ await vscode14.window.showErrorMessage(`Could not extract ${componentName}.stx.`);
6921
+ return;
6922
+ }
6923
+ const componentDocument = await vscode14.workspace.openTextDocument(componentUri);
6924
+ await vscode14.window.showTextDocument(componentDocument);
6925
+ });
6926
+ }
6771
6927
 
6772
6928
  // ../vscode/src/snippets.ts
6773
6929
  import * as fs5 from "node:fs";
6774
- import * as vscode14 from "vscode";
6930
+ import * as vscode15 from "vscode";
6775
6931
  function snippetCompletionItems(file) {
6776
6932
  let definitions;
6777
6933
  try {
@@ -6786,10 +6942,10 @@ function snippetCompletionItems(file) {
6786
6942
  const body = Array.isArray(snippet.body) ? snippet.body.join(`
6787
6943
  `) : snippet.body;
6788
6944
  for (const prefix of prefixes) {
6789
- const item = new vscode14.CompletionItem(prefix, vscode14.CompletionItemKind.Snippet);
6790
- item.insertText = new vscode14.SnippetString(body);
6945
+ const item = new vscode15.CompletionItem(prefix, vscode15.CompletionItemKind.Snippet);
6946
+ item.insertText = new vscode15.SnippetString(body);
6791
6947
  item.detail = snippet.description ?? name;
6792
- item.documentation = new vscode14.MarkdownString().appendCodeblock(body, "stx");
6948
+ item.documentation = new vscode15.MarkdownString().appendCodeblock(body, "stx");
6793
6949
  items.push(item);
6794
6950
  }
6795
6951
  }
@@ -6797,7 +6953,7 @@ function snippetCompletionItems(file) {
6797
6953
  }
6798
6954
  function registerSnippetCompletions(file) {
6799
6955
  const items = snippetCompletionItems(file);
6800
- return vscode14.languages.registerCompletionItemProvider("stx", {
6956
+ return vscode15.languages.registerCompletionItemProvider("stx", {
6801
6957
  provideCompletionItems: () => items
6802
6958
  });
6803
6959
  }
@@ -6816,32 +6972,32 @@ async function activateStxLanguage(context, options) {
6816
6972
  });
6817
6973
  componentRegistry.watchWorkspace(context);
6818
6974
  if (options.snippets)
6819
- context.subscriptions.push(registerSnippetCompletions(path6.join(options.assetsPath, "snippets", "stx.json")));
6820
- vscode15.workspace.textDocuments.forEach((document) => {
6975
+ context.subscriptions.push(registerSnippetCompletions(path8.join(options.assetsPath, "snippets", "stx.json")));
6976
+ vscode16.workspace.textDocuments.forEach((document) => {
6821
6977
  if (document.fileName.endsWith(".stx") && document.languageId !== "stx") {
6822
- vscode15.languages.setTextDocumentLanguage(document, "stx").then(() => console.log(`Set language ID for ${document.fileName} to stx`));
6978
+ vscode16.languages.setTextDocumentLanguage(document, "stx").then(() => console.log(`Set language ID for ${document.fileName} to stx`));
6823
6979
  }
6824
6980
  });
6825
- const hoverProvider = vscode15.languages.registerHoverProvider("stx", createHoverProvider(virtualTsDocumentProvider));
6826
- const definitionProvider = vscode15.languages.registerDefinitionProvider("stx", createDefinitionProvider(virtualTsDocumentProvider));
6827
- const documentLinkProvider = vscode15.languages.registerDocumentLinkProvider("stx", createDocumentLinkProvider());
6981
+ const hoverProvider = vscode16.languages.registerHoverProvider("stx", createHoverProvider(virtualTsDocumentProvider));
6982
+ const definitionProvider = vscode16.languages.registerDefinitionProvider("stx", createDefinitionProvider(virtualTsDocumentProvider));
6983
+ const documentLinkProvider = vscode16.languages.registerDocumentLinkProvider("stx", createDocumentLinkProvider());
6828
6984
  const completionProvider = createCompletionProvider();
6829
- const atTriggerCompletionProvider = vscode15.languages.registerCompletionItemProvider("stx", completionProvider, "@");
6830
- const parameterCompletionProvider = vscode15.languages.registerCompletionItemProvider("stx", completionProvider, "'", '"', "`", ",", ".");
6831
- const pathCompletionProvider = vscode15.languages.registerCompletionItemProvider("stx", createPathCompletionProvider(), "/", "'", '"');
6832
- context.subscriptions.push(vscode15.workspace.registerTextDocumentContentProvider("stx-ts", virtualTsDocumentProvider));
6985
+ const languageCompletionProvider = vscode16.languages.registerCompletionItemProvider("stx", completionProvider, "@", ":", "-", "'", '"', "`", ",", ".");
6986
+ const pathCompletionProvider = vscode16.languages.registerCompletionItemProvider("stx", createPathCompletionProvider(), "/", "'", '"');
6987
+ context.subscriptions.push(vscode16.workspace.registerTextDocumentContentProvider("stx-ts", virtualTsDocumentProvider));
6833
6988
  createDiagnosticsProvider(context);
6834
- const codeActionsProvider = vscode15.languages.registerCodeActionsProvider("stx", createCodeActionsProvider(), {
6989
+ const codeActionsProvider = vscode16.languages.registerCodeActionsProvider("stx", createCodeActionsProvider(), {
6835
6990
  providedCodeActionKinds: [
6836
- vscode15.CodeActionKind.QuickFix,
6837
- vscode15.CodeActionKind.RefactorRewrite,
6838
- vscode15.CodeActionKind.RefactorExtract
6991
+ vscode16.CodeActionKind.QuickFix,
6992
+ vscode16.CodeActionKind.RefactorRewrite,
6993
+ vscode16.CodeActionKind.RefactorExtract
6839
6994
  ]
6840
6995
  });
6841
- const foldingRangeProvider = vscode15.languages.registerFoldingRangeProvider("stx", createFoldingRangeProvider());
6842
- const semanticTokensProvider = vscode15.languages.registerDocumentSemanticTokensProvider("stx", createSemanticTokensProvider(), legend);
6996
+ const extractToComponentCommand = registerExtractToComponentCommand();
6997
+ const foldingRangeProvider = vscode16.languages.registerFoldingRangeProvider("stx", createFoldingRangeProvider());
6998
+ const semanticTokensProvider = vscode16.languages.registerDocumentSemanticTokensProvider("stx", createSemanticTokensProvider(), legend);
6843
6999
  let updateTimeout;
6844
- const documentChangeListener = vscode15.workspace.onDidChangeTextDocument((event) => {
7000
+ const documentChangeListener = vscode16.workspace.onDidChangeTextDocument((event) => {
6845
7001
  if (event.document.languageId === "stx" || event.document.languageId === "markdown" && event.document.fileName.endsWith(".md")) {
6846
7002
  clearTimeout(updateTimeout);
6847
7003
  updateTimeout = setTimeout(() => {
@@ -6849,7 +7005,7 @@ async function activateStxLanguage(context, options) {
6849
7005
  }, 150);
6850
7006
  }
6851
7007
  });
6852
- const documentOpenListener = vscode15.workspace.onDidOpenTextDocument(async (document) => {
7008
+ const documentOpenListener = vscode16.workspace.onDidOpenTextDocument(async (document) => {
6853
7009
  const isStx = document.languageId === "stx" || document.fileName.endsWith(".stx");
6854
7010
  const isMarkdown = document.languageId === "markdown" && document.fileName.endsWith(".md");
6855
7011
  if (!isStx && !isMarkdown)
@@ -6857,7 +7013,7 @@ async function activateStxLanguage(context, options) {
6857
7013
  virtualTsDocumentProvider.trackDocument(document);
6858
7014
  if (isStx && document.languageId !== "stx") {
6859
7015
  try {
6860
- await vscode15.languages.setTextDocumentLanguage(document, "stx");
7016
+ await vscode16.languages.setTextDocumentLanguage(document, "stx");
6861
7017
  } catch {}
6862
7018
  }
6863
7019
  const virtualUri = document.uri.with({
@@ -6865,12 +7021,12 @@ async function activateStxLanguage(context, options) {
6865
7021
  path: `${document.uri.path}.ts`
6866
7022
  });
6867
7023
  try {
6868
- await vscode15.workspace.openTextDocument(virtualUri);
7024
+ await vscode16.workspace.openTextDocument(virtualUri);
6869
7025
  } catch (error) {
6870
7026
  console.error("Failed to open virtual TypeScript document:", error);
6871
7027
  }
6872
7028
  });
6873
- vscode15.workspace.textDocuments.forEach((document) => {
7029
+ vscode16.workspace.textDocuments.forEach((document) => {
6874
7030
  if (document.languageId === "stx" || document.fileName.endsWith(".stx")) {
6875
7031
  virtualTsDocumentProvider.trackDocument(document);
6876
7032
  }
@@ -6878,8 +7034,8 @@ async function activateStxLanguage(context, options) {
6878
7034
  virtualTsDocumentProvider.trackDocument(document);
6879
7035
  }
6880
7036
  });
6881
- context.subscriptions.push(documentChangeListener, documentOpenListener, hoverProvider, definitionProvider, documentLinkProvider, atTriggerCompletionProvider, parameterCompletionProvider, pathCompletionProvider, codeActionsProvider, foldingRangeProvider, semanticTokensProvider);
6882
- const utilityClassesEnabled = vscode15.workspace.getConfiguration("stx.utilityClasses").get("enable", true);
7037
+ context.subscriptions.push(documentChangeListener, documentOpenListener, hoverProvider, definitionProvider, documentLinkProvider, languageCompletionProvider, pathCompletionProvider, codeActionsProvider, extractToComponentCommand, foldingRangeProvider, semanticTokensProvider);
7038
+ const utilityClassesEnabled = vscode16.workspace.getConfiguration("stx.utilityClasses").get("enable", true);
6883
7039
  if (utilityClassesEnabled) {
6884
7040
  try {
6885
7041
  await Promise.resolve().then(() => init_ts_css());
@@ -6887,7 +7043,7 @@ async function activateStxLanguage(context, options) {
6887
7043
  console.log("stx Extension - Css utility class features activated");
6888
7044
  } catch (error) {
6889
7045
  console.error("stx Extension - Failed to activate Css features:", error);
6890
- vscode15.window.showErrorMessage(`Failed to activate Css: ${error}`);
7046
+ vscode16.window.showErrorMessage(`Failed to activate Css: ${error}`);
6891
7047
  }
6892
7048
  }
6893
7049
  console.log("stx language support activated (with Markdown frontmatter support)");
@@ -6897,8 +7053,8 @@ function deactivateStxLanguage() {
6897
7053
  console.log("stx language support deactivated");
6898
7054
  }
6899
7055
  // ../vscode/src/extension.ts
6900
- import * as path7 from "node:path";
6901
- import * as vscode16 from "vscode";
7056
+ import * as path9 from "node:path";
7057
+ import * as vscode17 from "vscode";
6902
7058
 
6903
7059
  // ../vscode/src/stand-down.ts
6904
7060
  async function stacksProvidesStx(api) {
@@ -6940,15 +7096,15 @@ async function configureTypeScriptPlugin(api, subscriptions) {
6940
7096
 
6941
7097
  // ../vscode/src/extension.ts
6942
7098
  async function activate(context) {
6943
- configureTypeScriptPlugin(vscode16, context.subscriptions).catch((error) => {
7099
+ configureTypeScriptPlugin(vscode17, context.subscriptions).catch((error) => {
6944
7100
  console.error("stx Extension - could not configure the TypeScript plugin:", error);
6945
7101
  });
6946
- if (await stacksProvidesStx(vscode16)) {
7102
+ if (await stacksProvidesStx(vscode17)) {
6947
7103
  console.log(`stx Extension - ${STACKS_EXTENSION_ID} provides stx support in this window; not registering it twice`);
6948
7104
  return;
6949
7105
  }
6950
7106
  await activateStxLanguage(context, {
6951
- assetsPath: path7.join(context.extensionPath, "src"),
7107
+ assetsPath: path9.join(context.extensionPath, "src"),
6952
7108
  snippets: false
6953
7109
  });
6954
7110
  }
@@ -6961,6 +7117,9 @@ export {
6961
7117
  DEFAULT_TRANSITION_OPTIONS,
6962
7118
  PropsTypeExtractor,
6963
7119
  STACKS_EXTENSION_ID,
7120
+ STX_ATTRIBUTE_DIRECTIVES,
7121
+ STX_DIRECTIVES,
7122
+ STX_DIRECTIVE_PAIRS,
6964
7123
  STX_EXTENSION_ID,
6965
7124
  TS_PLUGIN_NAME,
6966
7125
  TS_PLUGIN_SETTINGS,
@@ -7002,6 +7161,10 @@ export {
7002
7161
  formatJSDoc,
7003
7162
  getClassAtPosition,
7004
7163
  getDefaultConfig as getCssDefaultConfig,
7164
+ getStxAttributeAtOffset,
7165
+ getStxAttributeCompletionContext,
7166
+ getStxAttributeDefinition,
7167
+ getStxDirectiveAtOffset,
7005
7168
  getTemplatePathRange,
7006
7169
  isColorClass,
7007
7170
  isCssClassName,
@@ -7017,6 +7180,7 @@ export {
7017
7180
  snippetCompletionItems,
7018
7181
  sortClasses,
7019
7182
  stacksProvidesStx,
7183
+ stxSnippetPreview,
7020
7184
  tokenModifiers,
7021
7185
  tokenTypes
7022
7186
  };