bunmicro 0.8.3 → 0.9.0

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.
@@ -3,7 +3,7 @@ filetype: javascript
3
3
  detect:
4
4
  filename: "(\\.(m|c)?js$|\\.es[5678]?$)"
5
5
  header: "^#!.*/(env +)?(bun|node)( |$)"
6
-
6
+ # console
7
7
  rules:
8
8
  - constant.number: "\\b[-+]?([1-9][0-9]*|0[0-7]*|0x[0-9a-fA-F]+)([uU][lL]?|[lL][uU]?)?\\b"
9
9
  - constant.number: "\\b[-+]?([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+)([EePp][+-]?[0-9]+)?[fFlL]?"
@@ -74,3 +74,51 @@ rules:
74
74
  # function documentation
75
75
  - identifier: "\\s\\*\\s.*"
76
76
  - todo: "(TODO|XXX|FIXME)"
77
+
78
+ # autocomplete-reference: Object constructor/static properties and methods (MDN Object)
79
+ # Object.length Object.name Object.prototype Object.assign Object.create Object.defineProperties Object.defineProperty Object.entries Object.freeze Object.fromEntries Object.getOwnPropertyDescriptor Object.getOwnPropertyDescriptors Object.getOwnPropertyNames Object.getOwnPropertySymbols Object.getPrototypeOf Object.groupBy Object.hasOwn Object.is Object.isExtensible Object.isFrozen Object.isSealed Object.keys Object.preventExtensions Object.seal Object.setPrototypeOf Object.values
80
+ # autocomplete-reference: Object.prototype properties and methods (MDN Object)
81
+ # Object.prototype.__proto__ Object.prototype.constructor Object.prototype.__defineGetter__ Object.prototype.__defineSetter__ Object.prototype.__lookupGetter__ Object.prototype.__lookupSetter__ Object.prototype.hasOwnProperty Object.prototype.isPrototypeOf Object.prototype.propertyIsEnumerable Object.prototype.toLocaleString Object.prototype.toString Object.prototype.valueOf
82
+
83
+
84
+ # autocomplete-reference: ECMAScript standard built-ins (MDN Global_Objects)
85
+ # globalThis Infinity NaN undefined eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape
86
+ # Object Function Boolean Symbol Error AggregateError EvalError RangeError ReferenceError SuppressedError SyntaxError TypeError URIError InternalError
87
+ # Number BigInt Math Date Temporal String RegExp
88
+ # Array TypedArray Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array BigInt64Array BigUint64Array Float16Array Float32Array Float64Array
89
+ # Map Set WeakMap WeakSet ArrayBuffer SharedArrayBuffer DataView Atomics JSON stringify parse WeakRef FinalizationRegistry
90
+ # Iterator AsyncIterator Promise GeneratorFunction AsyncGeneratorFunction Generator AsyncGenerator AsyncFunction DisposableStack AsyncDisposableStack
91
+ # Reflect Proxy Intl Collator DateTimeFormat DisplayNames DurationFormat ListFormat Locale NumberFormat PluralRules RelativeTimeFormat Segmenter
92
+ # autocomplete-reference: Browser globals and Web API interfaces (MDN Web/API)
93
+ # window self globalThis document navigator location history screen frames parent top opener origin name status closed crossOriginIsolated isSecureContext customElements visualViewport
94
+ # localStorage sessionStorage indexedDB caches cookieStore scheduler trustedTypes performance crypto console
95
+ # alert confirm prompt print open close focus blur stop find scroll scrollTo scrollBy scrollX scrollY moveTo moveBy resizeTo resizeBy
96
+ # getComputedStyle matchMedia requestAnimationFrame cancelAnimationFrame requestIdleCallback cancelIdleCallback structuredClone postMessage dispatchEvent addEventListener removeEventListener
97
+ # fetch setTimeout clearTimeout setInterval clearInterval queueMicrotask atob btoa createImageBitmap reportError
98
+ # AbortController AbortSignal AbstractRange AnalyserNode Animation AnimationEffect AnimationEvent AnimationPlaybackEvent AnimationTimeline Attr Audio AudioBuffer AudioBufferSourceNode AudioContext AudioData AudioDecoder AudioDestinationNode AudioEncoder AudioListener AudioNode AudioParam AudioParamMap AudioProcessingEvent AudioScheduledSourceNode AudioSinkInfo AudioWorklet AudioWorkletGlobalScope AudioWorkletNode AudioWorkletProcessor AuthenticatorAssertionResponse AuthenticatorAttestationResponse AuthenticatorResponse
99
+ # BarcodeDetector BatteryManager BeforeUnloadEvent BiquadFilterNode Blob BlobEvent Bluetooth BluetoothAdvertisingEvent BluetoothCharacteristicProperties BluetoothDevice BluetoothRemoteGATTCharacteristic BluetoothRemoteGATTDescriptor BluetoothRemoteGATTServer BluetoothRemoteGATTService BluetoothUUID BroadcastChannel BrowserCaptureMediaStreamTrack ByteLengthQueuingStrategy
100
+ # CSS CSSAnimation CSSConditionRule CSSContainerRule CSSCounterStyleRule CSSFontFaceRule CSSFontFeatureValuesRule CSSFontPaletteValuesRule CSSGroupingRule CSSImageValue CSSImportRule CSSKeyframeRule CSSKeyframesRule CSSKeywordValue CSSLayerBlockRule CSSLayerStatementRule CSSMathClamp CSSMathInvert CSSMathMax CSSMathMin CSSMathNegate CSSMathProduct CSSMathSum CSSMathValue CSSMatrixComponent CSSMediaRule CSSNamespaceRule CSSNumericArray CSSNumericValue CSSPageRule CSSPerspective CSSPositionValue CSSPropertyRule CSSRotate CSSRule CSSRuleList CSSScale CSSScopeRule CSSSkew CSSSkewX CSSSkewY CSSStartingStyleRule CSSStyleDeclaration CSSStyleRule CSSStyleSheet CSSStyleValue CSSSupportsRule CSSTransformComponent CSSTransformValue CSSTransition CSSTranslate CSSUnitValue CSSUnparsedValue CSSVariableReferenceValue
101
+ # Cache CacheStorage CanvasCaptureMediaStreamTrack CanvasGradient CanvasPattern CanvasRenderingContext2D CaptureController CaretPosition ChannelMergerNode ChannelSplitterNode CharacterData Clipboard ClipboardEvent ClipboardItem CloseEvent Comment CompositionEvent CompressionStream ConstantSourceNode ContentVisibilityAutoStateChangeEvent ConvolverNode CountQueuingStrategy Credential CredentialsContainer Crypto CryptoKey CustomElementRegistry CustomEvent
102
+ # DOMError DOMException DOMImplementation DOMMatrix DOMMatrixReadOnly DOMParser DOMPoint DOMPointReadOnly DOMQuad DOMRect DOMRectReadOnly DOMStringList DOMStringMap DOMTokenList DataTransfer DataTransferItem DataTransferItemList DecompressionStream DelayNode DeviceMotionEvent DeviceOrientationEvent DevicePosture DirectoryEntry DirectoryReader Document DocumentFragment DocumentPictureInPicture DocumentPictureInPictureEvent DocumentType DragEvent DynamicsCompressorNode
103
+ # EditContext Element ElementInternals EncodedAudioChunk EncodedVideoChunk ErrorEvent Event EventCounts EventSource EventTarget ExtendableCookieChangeEvent ExtendableEvent EyeDropper
104
+ # FeaturePolicy FederatedCredential Fence File FileList FileReader FileSystem FileSystemDirectoryEntry FileSystemDirectoryHandle FileSystemDirectoryReader FileSystemEntry FileSystemFileEntry FileSystemFileHandle FileSystemHandle FileSystemObserver FileSystemSyncAccessHandle FileSystemWritableFileStream FocusEvent FontData FontFace FontFaceSet FontFaceSetLoadEvent FormData FormDataEvent
105
+ # GPU GPUAdapter GPUAdapterInfo GPUBindGroup GPUBindGroupLayout GPUBuffer GPUCanvasContext GPUCommandBuffer GPUCommandEncoder GPUCompilationInfo GPUCompilationMessage GPUComputePassEncoder GPUComputePipeline GPUDevice GPUDeviceLostInfo GPUError GPUExternalTexture GPUInternalError GPUMapMode GPUOutOfMemoryError GPUPipelineError GPUPipelineLayout GPUQuerySet GPUQueue GPURenderBundle GPURenderBundleEncoder GPURenderPassEncoder GPURenderPipeline GPUSampler GPUShaderModule GPUSupportedFeatures GPUSupportedLimits GPUTexture GPUTextureView GPUUncapturedErrorEvent GPUValidationError Gamepad GamepadButton GamepadEvent GamepadHapticActuator Geolocation GeolocationCoordinates GeolocationPosition GeolocationPositionError GravitySensor Gyroscope
106
+ # HTMLAllCollection HTMLAnchorElement HTMLAreaElement HTMLAudioElement HTMLBRElement HTMLBaseElement HTMLBodyElement HTMLButtonElement HTMLCanvasElement HTMLCollection HTMLDListElement HTMLDataElement HTMLDataListElement HTMLDetailsElement HTMLDialogElement HTMLDivElement HTMLDocument HTMLElement HTMLEmbedElement HTMLFencedFrameElement HTMLFieldSetElement HTMLFontElement HTMLFormControlsCollection HTMLFormElement HTMLFrameElement HTMLFrameSetElement HTMLHRElement HTMLHeadElement HTMLHeadingElement HTMLHtmlElement HTMLIFrameElement HTMLImageElement HTMLInputElement HTMLLIElement HTMLLabelElement HTMLLegendElement HTMLLinkElement HTMLMapElement HTMLMarqueeElement HTMLMediaElement HTMLMenuElement HTMLMetaElement HTMLMeterElement HTMLModElement HTMLOListElement HTMLObjectElement HTMLOptGroupElement HTMLOptionElement HTMLOptionsCollection HTMLOutputElement HTMLParagraphElement HTMLParamElement HTMLPictureElement HTMLPreElement HTMLProgressElement HTMLQuoteElement HTMLScriptElement HTMLSelectElement HTMLSelectedContentElement HTMLSlotElement HTMLSourceElement HTMLSpanElement HTMLStyleElement HTMLTableCaptionElement HTMLTableCellElement HTMLTableColElement HTMLTableElement HTMLTableRowElement HTMLTableSectionElement HTMLTemplateElement HTMLTextAreaElement HTMLTimeElement HTMLTitleElement HTMLTrackElement HTMLUListElement HTMLUnknownElement HTMLVideoElement Headers Highlight HighlightRegistry History IDBCursor IDBCursorWithValue IDBDatabase IDBFactory IDBIndex IDBKeyRange IDBObjectStore IDBOpenDBRequest IDBRequest IDBTransaction IDBVersionChangeEvent IdleDeadline ImageBitmap ImageBitmapRenderingContext ImageCapture ImageData ImageDecoder ImageTrack ImageTrackList Ink InputDeviceCapabilities InputDeviceInfo InputEvent InstallEvent IntersectionObserver IntersectionObserverEntry
107
+ # Keyboard KeyboardEvent KeyboardLayoutMap KeyframeEffect LanguageDetector LargestContentfulPaint LaunchParams LaunchQueue LayoutShift LayoutShiftAttribution LinearAccelerationSensor Location Lock LockManager Magnetometer ManagedMediaSource ManagedSourceBuffer MathMLElement MediaCapabilities MediaDeviceInfo MediaDevices MediaElementAudioSourceNode MediaEncryptedEvent MediaError MediaKeyMessageEvent MediaKeys MediaKeySession MediaKeyStatusMap MediaKeySystemAccess MediaList MediaMetadata MediaQueryList MediaQueryListEvent MediaRecorder MediaRecorderErrorEvent MediaSession MediaSource MediaSourceHandle MediaStream MediaStreamAudioDestinationNode MediaStreamAudioSourceNode MediaStreamEvent MediaStreamTrack MediaStreamTrackAudioSourceNode MediaStreamTrackEvent MediaStreamTrackGenerator MediaStreamTrackProcessor MediaTrackConstraints MediaTrackSettings MediaTrackSupportedConstraints MessageChannel MessageEvent MessagePort MIDIAccess MIDIConnectionEvent MIDIInput MIDIInputMap MIDIMessageEvent MIDIOutput MIDIOutputMap MIDIPort MimeType MimeTypeArray MouseEvent MutationEvent MutationObserver MutationRecord
108
+ # NamedNodeMap NavigateEvent Navigation NavigationActivation NavigationCurrentEntryChangeEvent NavigationDestination NavigationHistoryEntry NavigationPrecommitController NavigationPreloadManager NavigationTransition Navigator NavigatorLogin NavigatorUAData NDEFMessage NDEFReader NDEFReadingEvent NDEFRecord NetworkInformation Node NodeIterator NodeList Notification NotificationEvent OffscreenCanvas OfflineAudioCompletionEvent OfflineAudioContext OrientationSensor OscillatorNode OverconstrainedError PageRevealEvent PageSwapEvent PageTransitionEvent PannerNode PasswordCredential Path2D PaymentAddress PaymentMethodChangeEvent PaymentRequest PaymentRequestUpdateEvent PaymentResponse Performance PerformanceElementTiming PerformanceEntry PerformanceEventTiming PerformanceLongAnimationFrameTiming PerformanceLongTaskTiming PerformanceMark PerformanceMeasure PerformanceNavigationTiming PerformanceObserver PerformanceObserverEntryList PerformancePaintTiming PerformanceResourceTiming PerformanceScriptTiming PerformanceServerTiming PerformanceTiming PeriodicSyncManager Permissions PermissionStatus PictureInPictureEvent PictureInPictureWindow Plugin PluginArray PointerEvent PopStateEvent Presentation PresentationAvailability PresentationConnection PresentationConnectionAvailableEvent PresentationConnectionCloseEvent PresentationConnectionList PresentationReceiver PresentationRequest PressureObserver PressureRecord ProcessingInstruction ProgressEvent PromiseRejectionEvent PublicKeyCredential PushEvent PushManager PushMessageData PushSubscription PushSubscriptionOptions
109
+ # RTCCertificate RTCDTMFSender RTCDTMFToneChangeEvent RTCDataChannel RTCDataChannelEvent RTCDtlsTransport RTCEncodedAudioFrame RTCEncodedVideoFrame RTCError RTCErrorEvent RTCIceCandidate RTCIceTransport RTCPeerConnection RTCPeerConnectionIceErrorEvent RTCPeerConnectionIceEvent RTCRtpReceiver RTCRtpScriptTransformer RTCRtpSender RTCRtpTransceiver RTCSctpTransport RTCSessionDescription RTCStatsReport RTCTrackEvent RadioNodeList Range ReadableByteStreamController ReadableStream ReadableStreamBYOBReader ReadableStreamBYOBRequest ReadableStreamDefaultController ReadableStreamDefaultReader RelativeOrientationSensor RemotePlayback Report ReportBody ReportingObserver Request ResizeObserver ResizeObserverEntry ResizeObserverSize Response SVGAElement SVGAngle SVGAnimateElement SVGAnimateMotionElement SVGAnimateTransformElement SVGAnimatedAngle SVGAnimatedBoolean SVGAnimatedEnumeration SVGAnimatedInteger SVGAnimatedLength SVGAnimatedLengthList SVGAnimatedNumber SVGAnimatedNumberList SVGAnimatedPreserveAspectRatio SVGAnimatedRect SVGAnimatedString SVGAnimatedTransformList SVGAnimationElement SVGCircleElement SVGClipPathElement SVGComponentTransferFunctionElement SVGDefsElement SVGDescElement SVGElement SVGEllipseElement SVGFEBlendElement SVGFEColorMatrixElement SVGFEComponentTransferElement SVGFECompositeElement SVGFEConvolveMatrixElement SVGFEDiffuseLightingElement SVGFEDisplacementMapElement SVGFEDistantLightElement SVGFEDropShadowElement SVGFEFloodElement SVGFEFuncAElement SVGFEFuncBElement SVGFEFuncGElement SVGFEFuncRElement SVGFEGaussianBlurElement SVGFEImageElement SVGFEMergeElement SVGFEMergeNodeElement SVGFEMorphologyElement SVGFEOffsetElement SVGFEPointLightElement SVGFESpecularLightingElement SVGFESpotLightElement SVGFETileElement SVGFETurbulenceElement SVGFilterElement SVGForeignObjectElement SVGGElement SVGGeometryElement SVGGradientElement SVGGraphicsElement SVGImageElement SVGLength SVGLengthList SVGLineElement SVGLinearGradientElement SVGMPathElement SVGMarkerElement SVGMaskElement SVGMatrix SVGMetadataElement SVGNumber SVGNumberList SVGPathElement SVGPatternElement SVGPoint SVGPointList SVGPolygonElement SVGPolylineElement SVGPreserveAspectRatio SVGRadialGradientElement SVGRect SVGRectElement SVGSVGElement SVGScriptElement SVGSetElement SVGStopElement SVGStringList SVGStyleElement SVGSwitchElement SVGSymbolElement SVGTSpanElement SVGTextContentElement SVGTextElement SVGTextPathElement SVGTextPositioningElement SVGTitleElement SVGTransform SVGTransformList SVGUnitTypes SVGUseElement SVGViewElement Screen ScreenDetailed ScreenDetails ScreenOrientation ScriptProcessorNode SecurityPolicyViolationEvent Selection Sensor SensorErrorEvent Serial SerialPort ServiceWorker ServiceWorkerContainer ServiceWorkerGlobalScope ServiceWorkerRegistration ShadowRoot SharedWorker SourceBuffer SourceBufferList SpeechRecognition SpeechRecognitionAlternative SpeechRecognitionErrorEvent SpeechRecognitionEvent SpeechRecognitionResult SpeechRecognitionResultList SpeechSynthesis SpeechSynthesisErrorEvent SpeechSynthesisEvent SpeechSynthesisUtterance SpeechSynthesisVoice StaticRange StereoPannerNode Storage StorageEvent StorageManager StylePropertyMap StylePropertyMapReadOnly StyleSheet StyleSheetList SubmitEvent SubtleCrypto SyncManager
110
+ # TaskAttributionTiming TaskController TaskPriorityChangeEvent TaskSignal Text TextDecoder TextEncoder TextDecoderStream TextEncoderStream TextEvent TextFormat TextFormatUpdateEvent TextMetrics TextTrack TextTrackCue TextTrackCueList TextTrackList TimeRanges ToggleEvent Touch TouchEvent TouchList TrackEvent TransformStream TransformStreamDefaultController TransitionEvent TreeWalker TrustedHTML TrustedScript TrustedScriptURL TrustedTypePolicy TrustedTypePolicyFactory UIEvent URL URLPattern URLSearchParams USB USBAlternateInterface USBConfiguration USBConnectionEvent USBDevice USBEndpoint USBInTransferResult USBInterface USBIsochronousInTransferPacket USBIsochronousInTransferResult USBIsochronousOutTransferPacket USBOutTransferResult UserActivation VTTCue VTTRegion ValidityState VideoColorSpace VideoDecoder VideoEncoder VideoFrame VideoPlaybackQuality ViewTransition VirtualKeyboard VisualViewport WakeLock WakeLockSentinel WebAssembly WebGL2RenderingContext WebGLActiveInfo WebGLBuffer WebGLContextEvent WebGLFramebuffer WebGLProgram WebGLQuery WebGLRenderbuffer WebGLRenderingContext WebGLSampler WebGLShader WebGLShaderPrecisionFormat WebGLSync WebGLTexture WebGLTransformFeedback WebGLUniformLocation WebGLVertexArrayObject WebSocket WebSocketStream WebTransport WebTransportBidirectionalStream WebTransportDatagramDuplexStream WebTransportError WheelEvent Window WindowClient WindowControlsOverlay Worker WorkerGlobalScope WorkerLocation WorkerNavigator Worklet WorkletGlobalScope WritableStream WritableStreamDefaultController WritableStreamDefaultWriter XMLDocument XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload XMLSerializer XPathEvaluator XPathExpression XPathResult XRAnchor XRAnchorSet XRBoundedReferenceSpace XRCPUDepthInformation XRCamera XRDOMOverlayState XRDepthInformation XRFrame XRHand XRHitTestResult XRHitTestSource XRInputSource XRInputSourceArray XRInputSourceEvent XRInputSourcesChangeEvent XRJointPose XRJointSpace XRLayer XRLightEstimate XRLightProbe XRPose XRRay XRReferenceSpace XRReferenceSpaceEvent XRRenderState XRRigidTransform XRSession XRSessionEvent XRSpace XRSystem XRTransientInputHitTestResult XRTransientInputHitTestSource XRView XRViewerPose XRViewport XRWebGLBinding XRWebGLDepthInformation XRWebGLLayer XSLTProcessor
111
+ # autocomplete-reference: Node.js globals and builtin modules (Node.js API)
112
+ # __dirname __filename exports module require process console Buffer
113
+ # AbortController AbortSignal Blob BroadcastChannel ByteLengthQueuingStrategy CloseEvent CompressionStream CountQueuingStrategy Crypto CryptoKey CustomEvent DecompressionStream DOMException ErrorEvent Event EventSource EventTarget File FormData Headers localStorage MessageChannel MessageEvent MessagePort navigator PerformanceEntry PerformanceMark PerformanceMeasure PerformanceObserver PerformanceObserverEntryList PerformanceResourceTiming performance Request Response sessionStorage Storage structuredClone SubtleCrypto TextDecoder TextDecoderStream TextEncoder TextEncoderStream TransformStream URL URLPattern URLSearchParams WebAssembly WebSocket WritableStream WritableStreamDefaultController WritableStreamDefaultWriter ReadableStream ReadableStreamBYOBReader ReadableStreamBYOBRequest ReadableStreamDefaultController ReadableStreamDefaultReader
114
+ # node assert assert/strict async_hooks buffer child_process cluster console constants crypto dgram diagnostics_channel dns dns/promises domain events fs fs/promises http http2 https inspector inspector/promises module net os path path/posix path/win32 perf_hooks process punycode querystring readline readline/promises repl sea sqlite stream stream/consumers stream/promises stream/web string_decoder test test/reporters timers timers/promises tls trace_events tty url util util/types v8 vm wasi worker_threads zlib
115
+ # autocomplete-reference: Bun globals and Bun native APIs (Bun docs)
116
+ # Bun bun jsc gc CryptoHasher FileSink HTMLBundle Shell ShellError ShellOutput ShellPromise Subprocess Transpiler UnsafeCallback build color console depth dns file gc gzip hash indexOfLine lineColumn listen main mmap nanoseconds openInEditor origin peek readableStreamToArray readableStreamToBlob readableStreamToBytes readableStreamToFormData readableStreamToJSON readableStreamToText resolve resolveSync revision semver serve shell sleep sleepSync spawn spawnSync stderr stdin stdout stringWidth stripANSI udp unzip version which write
117
+ # BunFile S3File ArrayBufferSink BlobPart BodyInit BunPlugin BuildConfig BuildMessage BuildOutput BuildArtifact CompileBuildConfig Cookie CookieMap Env FileBlob FileSystemRouter Glob GlobScanOptions HTTPResponseSink ImportMeta InspectOptions Mock ModuleMock NetworkSink PathLike PluginBuilder Server ServerWebSocket Socket TCPSocketListener TLSOptions TranspilerOptions UDPConnectedSocket UDPSocket WebSocketHandler WebSocketServeOptions
118
+ # fetch Request Response Headers FormData Blob File URL URLSearchParams TextEncoder TextDecoder ReadableStream WritableStream TransformStream WebSocket Event EventTarget MessageEvent CloseEvent ErrorEvent CustomEvent DOMException AbortController AbortSignal crypto Crypto CryptoKey SubtleCrypto performance structuredClone queueMicrotask setTimeout clearTimeout setInterval clearInterval setImmediate clearImmediate
119
+ # bun:test describe test it expect beforeAll beforeEach afterAll afterEach mock spyOn jest setSystemTime restore clearAllMocks restoreAllMocks
120
+ # bun:sqlite Database Statement SQLQueryBindings SQLQueryResult
121
+ # bun:jsc serialize deserialize
122
+ # bun:ffi dlopen suffix ptr CString JSCallback FFIType
123
+ # bun:wrap wrap
124
+ # bun:main bun:internal bun:macro bun:plugin bun:embedded bun:generated
@@ -60,3 +60,21 @@ rules:
60
60
  end: "$"
61
61
  rules: # AKA Code tags (PEP 350)
62
62
  - todo: "(TODO|FIXME|HACK|BUG|NOTE|FAQ|MNEMONIC|REQ|RFE|IDEA|PORT|\\?\\?\\?|!!!|GLOSS|SEE|TODOC|STAT|RVD|CRED):?"
63
+
64
+ # autocomplete-reference: Python built-in constants, functions, and types (Python docs)
65
+ # None True False Ellipsis NotImplemented __debug__ abs aiter all anext any ascii bin bool breakpoint bytearray bytes callable chr classmethod compile complex delattr dict dir divmod enumerate eval exec filter float format frozenset getattr globals hasattr hash help hex id input int isinstance issubclass iter len list locals map max memoryview min next object oct open ord pow print property range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip __import__
66
+ # ArithmeticError AssertionError AttributeError BaseException BaseExceptionGroup BlockingIOError BrokenPipeError BufferError BytesWarning ChildProcessError ConnectionAbortedError ConnectionError ConnectionRefusedError ConnectionResetError DeprecationWarning EOFError EncodingWarning EnvironmentError Exception ExceptionGroup FileExistsError FileNotFoundError FloatingPointError FutureWarning GeneratorExit IOError ImportError ImportWarning IndentationError IndexError InterruptedError IsADirectoryError KeyError KeyboardInterrupt LookupError MemoryError ModuleNotFoundError NameError NotADirectoryError NotImplementedError OSError OverflowError PendingDeprecationWarning PermissionError ProcessLookupError RecursionError ReferenceError ResourceWarning RuntimeError RuntimeWarning StopAsyncIteration StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError TimeoutError TypeError UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning ValueError Warning ZeroDivisionError
67
+ # autocomplete-reference: Python common object and data model methods
68
+ # __abs__ __add__ __aiter__ __all__ __and__ __annotations__ __anext__ __await__ __bases__ __bool__ __buffer__ __builtins__ __bytes__ __cached__ __call__ __ceil__ __class__ __class_getitem__ __closure__ __code__ __complex__ __contains__ __context__ __debug__ __defaults__ __del__ __delattr__ __delete__ __delitem__ __dict__ __dir__ __divmod__ __doc__ __enter__ __eq__ __exit__ __file__ __float__ __floor__ __floordiv__ __format__ __fspath__ __ge__ __get__ __getattr__ __getattribute__ __getitem__ __getnewargs__ __getstate__ __globals__ __gt__ __hash__ __iadd__ __iand__ __ifloordiv__ __ilshift__ __imatmul__ __imod__ __imul__ __index__ __init__ __init_subclass__ __instancecheck__ __int__ __invert__ __ior__ __ipow__ __irshift__ __isub__ __iter__ __itruediv__ __ixor__ __kwdefaults__ __le__ __len__ __length_hint__ __loader__ __lshift__ __lt__ __matmul__ __missing__ __mod__ __module__ __mro__ __mul__ __name__ __ne__ __neg__ __new__ __next__ __or__ __package__ __path__ __pos__ __pow__ __qualname__ __radd__ __rand__ __rdivmod__ __reduce__ __reduce_ex__ __repr__ __reversed__ __rfloordiv__ __rlshift__ __rmatmul__ __rmod__ __rmul__ __ror__ __round__ __rpow__ __rrshift__ __rshift__ __rsub__ __rtruediv__ __rxor__ __set__ __set_name__ __setattr__ __setitem__ __slots__ __spec__ __str__ __sub__ __subclasscheck__ __subclasses__ __traceback__ __truediv__ __trunc__ __weakref__ __xor__
69
+ # autocomplete-reference: Python str, bytes, list, tuple, dict, set, file/path methods
70
+ # capitalize casefold center count encode endswith expandtabs find format format_map index isalnum isalpha isascii isdecimal isdigit isidentifier islower isnumeric isprintable isspace istitle isupper join ljust lower lstrip maketrans partition removeprefix removesuffix replace rfind rindex rjust rpartition rsplit rstrip split splitlines startswith strip swapcase title translate upper zfill
71
+ # append clear copy count extend index insert pop remove reverse sort
72
+ # clear copy fromkeys get items keys pop popitem setdefault update values
73
+ # add clear copy difference difference_update discard intersection intersection_update isdisjoint issubset issuperset pop remove symmetric_difference symmetric_difference_update union update
74
+ # close closed detach fileno flush isatty read readable readline readlines seek seekable tell truncate writable write writelines exists is_file is_dir iterdir glob rglob mkdir rename replace resolve absolute parent parents name stem suffix suffixes with_name with_stem with_suffix open read_text read_bytes write_text write_bytes
75
+ # autocomplete-reference: Python standard-library module names (Python module index)
76
+ # abc aifc argparse array ast asyncio atexit audioop base64 bdb binascii bisect builtins bz2 calendar cgi cgitb chunk cmath cmd code codecs codeop collections colorsys compileall concurrent configparser contextlib contextvars copy copyreg crypt csv ctypes curses dataclasses datetime dbm decimal difflib dis doctest email encodings ensurepip enum errno faulthandler fcntl filecmp fileinput fnmatch fractions ftplib functools gc getopt getpass gettext glob graphlib grp gzip hashlib heapq hmac html http idlelib imaplib imghdr importlib inspect io ipaddress itertools json keyword linecache locale logging lzma mailbox mailcap marshal math mimetypes mmap modulefinder multiprocessing netrc nis nntplib numbers operator optparse os pathlib pdb pickle pickletools pipes pkgutil platform plistlib poplib posix pprint profile pstats pty pwd py_compile pyclbr pydoc queue quopri random re readline reprlib resource rlcompleter runpy sched secrets select selectors shelve shlex shutil signal site smtpd smtplib sndhdr socket socketserver sqlite3 ssl stat statistics string stringprep struct subprocess sunau symtable sys sysconfig syslog tabnanny tarfile telnetlib tempfile termios textwrap threading time timeit tkinter token tokenize tomllib trace traceback tracemalloc tty turtle types typing unicodedata unittest urllib uuid venv warnings wave weakref webbrowser wsgiref xdrlib xml xmlrpc zipapp zipfile zipimport zlib zoneinfo
77
+ # autocomplete-reference: famous third-party Python packages and common aliases
78
+ # numpy np pandas pd scipy sklearn scikit_learn matplotlib plt seaborn sns plotly bokeh altair statsmodels sympy numba cython polars pyarrow dask xarray networkx requests httpx aiohttp urllib3 beautifulsoup4 bs4 lxml scrapy selenium playwright flask django fastapi starlette pydantic sqlalchemy alembic psycopg2 pymongo redis celery typer click rich textual tqdm pytest hypothesis tox nox black ruff flake8 mypy pyright isort poetry pip setuptools wheel virtualenv ipython jupyter notebook jupyterlab ipykernel ipywidgets traitlets pillow PIL opencv cv2 imageio skimage scikit_image torch torchvision torchaudio tensorflow keras jax flax transformers tokenizers datasets accelerate diffusers sentence_transformers spacy nltk gensim langchain openai anthropic google genai boto3 botocore s3fs fsspec cryptography paramiko fabric pendulum arrow dateutil pyyaml yaml toml tomli tomlkit orjson ujson msgpack pydantic_settings python_dotenv dotenv sqlalchemy_utils flask_sqlalchemy django_rest_framework drf pytest_asyncio pytest_cov coverage
79
+ # autocomplete-reference: common package submodules and method words
80
+ # ndarray array arange linspace zeros ones empty full eye reshape transpose astype dtype shape ndim size mean median std var sum min max argmin argmax argsort sort clip concatenate stack hstack vstack dot matmul einsum where unique random DataFrame Series read_csv read_excel read_json read_parquet read_sql to_csv to_excel to_json to_parquet groupby merge join concat pivot pivot_table drop dropna fillna assign apply map iloc loc values columns index describe head tail info query sort_values reset_index set_index fit transform fit_transform predict predict_proba score train_test_split Pipeline GridSearchCV StandardScaler OneHotEncoder RandomForestClassifier RandomForestRegressor LinearRegression LogisticRegression
@@ -29,11 +29,12 @@ export class SyntaxHeader {
29
29
  }
30
30
 
31
31
  export class SyntaxDefinition {
32
- constructor(header, source) {
32
+ constructor(header, source, text = "") {
33
33
  this.header = header;
34
34
  this.filetype = header.filetype;
35
35
  this.source = source;
36
36
  this.rules = parseRules(source.rules ?? []);
37
+ this.autocompleteWords = scanAutocompleteWordsFromText(text);
37
38
  }
38
39
  }
39
40
 
@@ -45,15 +46,27 @@ export async function loadSyntaxDefinitions(runtime) {
45
46
 
46
47
  const definitions = [];
47
48
  for (const file of runtime.list(1)) {
48
- try{
49
- const source = Bun.YAML.parse(await file.text());
50
- const header = headers.get(file.name) ?? parseHeaderYaml(source);
51
- definitions.push(new SyntaxDefinition(header, source));
52
- }catch(e){
53
- console.error("Failed to parse syntax yaml:",file.name)
54
- console.error(" Will not highlight this kind of file")
55
- console.error(" @ loadSyntaxDefinitions ")
49
+ let text = "";
50
+ try {
51
+ text = await file.text();
52
+ } catch (e) {
53
+ console.error("Failed to read syntax yaml:", file.name);
54
+ console.error(" Will not highlight this kind of file");
55
+ console.error(" @ loadSyntaxDefinitions ");
56
+ continue;
56
57
  }
58
+
59
+ let source = null;
60
+ try {
61
+ source = Bun.YAML.parse(text);
62
+ } catch (e) {
63
+ console.error("Failed to parse syntax yaml:", file.name);
64
+ console.error(" Will not highlight this kind of file");
65
+ console.error(" @ loadSyntaxDefinitions ");
66
+ }
67
+
68
+ const header = headers.get(file.name) ?? (source ? parseHeaderYaml(source) : parseHeaderTextFallback(text, file.name));
69
+ definitions.push(new SyntaxDefinition(header, source ?? { rules: [] }, text));
57
70
  }
58
71
  return definitions;
59
72
  }
@@ -86,6 +99,61 @@ export function parseHeaderYaml(source) {
86
99
  });
87
100
  }
88
101
 
102
+ function parseHeaderTextFallback(text, fileName = "") {
103
+ const source = String(text ?? "");
104
+ const fallbackType = String(fileName).replace(/\.ya?ml$/i, "");
105
+ const filetype = rawYamlScalar(source.match(/^filetype:[ \t]*(.*)$/m)?.[1]) || fallbackType;
106
+ return new SyntaxHeader({
107
+ filetype,
108
+ filename: rawYamlDetectScalar(source, "filename"),
109
+ header: rawYamlDetectScalar(source, "header"),
110
+ signature: rawYamlDetectScalar(source, "signature"),
111
+ });
112
+ }
113
+
114
+ function rawYamlDetectScalar(text, key) {
115
+ const detect = text.match(/^detect:[ \t]*(?:#.*)?(?:\r?\n)((?:[ \t]+[^\n]*\r?\n?)*)/m)?.[1] ?? "";
116
+ return rawYamlScalar(detect.match(new RegExp(`^[ \t]+${key}:[ \t]*(.*)$`, "m"))?.[1]);
117
+ }
118
+
119
+ function rawYamlScalar(value) {
120
+ if (value == null) return "";
121
+ let out = String(value).trim();
122
+ if (!out) return "";
123
+ if (out.startsWith("\"") && out.endsWith("\"")) {
124
+ try { return JSON.parse(out); } catch { return out.slice(1, -1); }
125
+ }
126
+ if (out.startsWith("'") && out.endsWith("'")) return out.slice(1, -1).replaceAll("''", "'");
127
+ return out;
128
+ }
129
+
130
+ function scanAutocompleteWordsFromText(text) {
131
+ const source = String(text ?? "");
132
+ const words = [];
133
+ const seen = new Set();
134
+ let i = 0;
135
+ while (i < source.length) {
136
+ if (!isSyntaxWordChar(source[i])) { i++; continue; }
137
+ let j = i;
138
+ while (j < source.length && isSyntaxWordChar(source[j])) j++;
139
+ const word = source.slice(i, j);
140
+ if (!seen.has(word)) {
141
+ seen.add(word);
142
+ words.push(word);
143
+ }
144
+ i = j;
145
+ }
146
+ return words;
147
+ }
148
+
149
+ function isSyntaxWordChar(ch) {
150
+ if (!ch) return false;
151
+ const cp = ch.codePointAt(0);
152
+ if ((cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || (cp >= 48 && cp <= 57) || cp === 95) return true;
153
+ if (cp <= 127) return false;
154
+ return /\p{L}|\p{N}/u.test(ch);
155
+ }
156
+
89
157
  function parseRules(rules) {
90
158
  return rules.map((rule) => parseRule(rule)).filter(Boolean);
91
159
  }
package/src/index.js CHANGED
@@ -190,25 +190,79 @@ function isWideCodePoint(cp) {
190
190
  (cp >= 0x1F004 && cp <= 0x1F0CF) ||
191
191
  (cp >= 0x1F18F && cp <= 0x1F19A) ||
192
192
  (cp >= 0x1F200 && cp <= 0x1F2FF) ||
193
- (cp >= 0x1F300 && cp <= 0x1F64F) ||
194
- (cp >= 0x1F900 && cp <= 0x1F9FF) ||
193
+ (cp >= 0x1F300 && cp <= 0x1FAFF) ||
195
194
  (cp >= 0x20000 && cp <= 0x2FFFD) ||
196
195
  (cp >= 0x30000 && cp <= 0x3FFFD)
197
196
  );
198
197
  }
199
198
 
199
+ function isZeroWidthCodePoint(cp) {
200
+ return (
201
+ cp === 0x200D ||
202
+ (cp >= 0x0300 && cp <= 0x036F) ||
203
+ (cp >= 0x1AB0 && cp <= 0x1AFF) ||
204
+ (cp >= 0x1DC0 && cp <= 0x1DFF) ||
205
+ (cp >= 0x20D0 && cp <= 0x20FF) ||
206
+ (cp >= 0xFE00 && cp <= 0xFE0F) ||
207
+ (cp >= 0xFE20 && cp <= 0xFE2F) ||
208
+ (cp >= 0xE0100 && cp <= 0xE01EF)
209
+ );
210
+ }
211
+
200
212
  function charWidth(ch) {
201
213
  if (!ch) return 0;
202
214
  const cp = ch.codePointAt(0);
203
215
  if (cp === 9) return DEFAULT_SETTINGS.tabsize;
204
- if (cp < 32 || (cp >= 0x7f && cp < 0xa0)) return 0;
216
+ if (cp < 32 || (cp >= 0x7f && cp < 0xa0) || isZeroWidthCodePoint(cp)) return 0;
205
217
  if (isWideCodePoint(cp)) return 2;
206
218
  return 1;
207
219
  }
208
220
 
221
+ function isEmojiVariationBase(cp) {
222
+ return (
223
+ cp === 0x00A9 || cp === 0x00AE ||
224
+ cp === 0x203C || cp === 0x2049 ||
225
+ cp === 0x2122 || cp === 0x2139 ||
226
+ (cp >= 0x2194 && cp <= 0x21AA) ||
227
+ (cp >= 0x231A && cp <= 0x231B) ||
228
+ cp === 0x2328 || cp === 0x23CF ||
229
+ (cp >= 0x23E9 && cp <= 0x23F3) ||
230
+ (cp >= 0x23F8 && cp <= 0x23FA) ||
231
+ cp === 0x24C2 ||
232
+ (cp >= 0x25AA && cp <= 0x25AB) ||
233
+ cp === 0x25B6 || cp === 0x25C0 ||
234
+ (cp >= 0x25FB && cp <= 0x25FE) ||
235
+ (cp >= 0x2600 && cp <= 0x27BF) ||
236
+ (cp >= 0x2934 && cp <= 0x2935) ||
237
+ (cp >= 0x2B05 && cp <= 0x2B55) ||
238
+ cp === 0x3030 || cp === 0x303D ||
239
+ cp === 0x3297 || cp === 0x3299
240
+ );
241
+ }
242
+
243
+ function displayUnitAt(text, idx) {
244
+ const cp = text.codePointAt(idx);
245
+ if (cp == null) return { text: "", width: 0, length: 0 };
246
+ let length = cp > 0xFFFF ? 2 : 1;
247
+ let unit = String.fromCodePoint(cp);
248
+ let width = charWidth(unit);
249
+ const nextCp = text.codePointAt(idx + length);
250
+ if (nextCp === 0xFE0F && isEmojiVariationBase(cp)) {
251
+ unit += String.fromCodePoint(nextCp);
252
+ length += 1;
253
+ width = 2;
254
+ }
255
+ return { text: unit, width, length };
256
+ }
257
+
209
258
  function displayWidth(text) {
210
259
  let width = 0;
211
- for (const ch of text) width += charWidth(ch);
260
+ for (let i = 0; i < text.length;) {
261
+ const unit = displayUnitAt(text, i);
262
+ if (unit.length <= 0) break;
263
+ width += unit.width;
264
+ i += unit.length;
265
+ }
212
266
  return width;
213
267
  }
214
268
 
@@ -260,6 +314,16 @@ function charIdxForScrollRight(line, cursorX, visibleCols) {
260
314
  return i;
261
315
  }
262
316
 
317
+ function normalizeCharBoundary(line, idx) {
318
+ idx = clamp(idx, 0, line.length);
319
+ if (idx > 0 && idx < line.length) {
320
+ const prev = line.charCodeAt(idx - 1);
321
+ const cur = line.charCodeAt(idx);
322
+ if (prev >= 0xD800 && prev <= 0xDBFF && cur >= 0xDC00 && cur <= 0xDFFF) return idx - 1;
323
+ }
324
+ return idx;
325
+ }
326
+
263
327
  // --- Softwrap utilities (ported from Go internal/display/softwrap.go) ---
264
328
 
265
329
  // Returns an array of code-unit indices where each visual row starts.
@@ -581,7 +645,7 @@ class BufferModel {
581
645
 
582
646
  ensureCursor() {
583
647
  this.cursor.y = clamp(this.cursor.y, 0, this.lines.length - 1);
584
- this.cursor.x = clamp(this.cursor.x, 0, this.line().length);
648
+ this.cursor.x = normalizeCharBoundary(this.line(), this.cursor.x);
585
649
  }
586
650
 
587
651
  invalidateHighlightFrom(lineNo = 0, options = {}) {
@@ -1038,6 +1102,13 @@ class BufferModel {
1038
1102
  }
1039
1103
  }
1040
1104
  }
1105
+ const syntaxWords = this.syntaxDefinition?.autocompleteWords ?? [];
1106
+ for (const w of syntaxWords) {
1107
+ if (w.length > wordLen && w.startsWith(word) && !seen.has(w)) {
1108
+ seen.add(w);
1109
+ suggestions.push(w);
1110
+ }
1111
+ }
1041
1112
  if (suggestions.length === 0) return false;
1042
1113
  if (suggestions.length === 1) {
1043
1114
  // Single match: insert suffix directly without entering cycling mode
@@ -1252,6 +1323,7 @@ class TerminalPane {
1252
1323
  this.app = app;
1253
1324
  this.proc = null;
1254
1325
  this.vt = null;
1326
+ this.decoder = new TextDecoder();
1255
1327
  }
1256
1328
 
1257
1329
  open(cols, rows) {
@@ -1269,7 +1341,8 @@ class TerminalPane {
1269
1341
  cols,
1270
1342
  rows,
1271
1343
  data: (_terminal, data) => {
1272
- const text = decoder.decode(data);
1344
+ const text = this.decoder.decode(data, { stream: true });
1345
+ if (!text) return;
1273
1346
  const responses = this.vt.feed(text);
1274
1347
  for (const resp of responses) this.proc?.terminal?.write(resp);
1275
1348
  this.app.render();
@@ -1464,9 +1537,10 @@ class App {
1464
1537
  const resize = this.screen.updateSize();
1465
1538
  this.rows = resize.rows;
1466
1539
  this.cols = resize.cols;
1540
+ this.layoutEditorArea();
1467
1541
  for (const tab of this.tabs)
1468
1542
  for (const p of tab.panes())
1469
- if (p.type === "term") p.terminal?.resize(p.w, Math.max(4, p.h));
1543
+ if (p.type === "term") p.terminal?.resize(p.w, Math.max(4, p.h - 1));
1470
1544
  if (!this.shellRunning && !this._alertRunning) this.render();
1471
1545
  });
1472
1546
  process.on("SIGINT", () => {}); // Ctrl+C is handled as copy in handleEvent
@@ -1500,9 +1574,7 @@ class App {
1500
1574
  process.exit(code);
1501
1575
  }
1502
1576
 
1503
- render() {
1504
- if (!this.running) return;
1505
- const tab = this.tab;
1577
+ layoutEditorArea() {
1506
1578
  const promptHeight = this.prompt ? 1 : 0;
1507
1579
  const tabBarHeight = this.tabs.length > 1 ? 1 : 0;
1508
1580
  const keymenuHeight = this.keymenu ? KEYDISPLAY.length : 0;
@@ -1517,15 +1589,40 @@ class App {
1517
1589
  const editorAreaH = Math.max(1, this.rows - 1 - promptHeight - tabBarHeight - keymenuHeight - infoHeight);
1518
1590
  const statusRow = this.rows - promptHeight - 1;
1519
1591
 
1592
+ for (const tab of this.tabs) computeLayout(tab.root, 0, editorAreaTop, this.cols, editorAreaH);
1593
+
1594
+ return {
1595
+ tabBarHeight,
1596
+ keymenuHeight,
1597
+ activeSuggestions,
1598
+ activeSuggestionIdx,
1599
+ activeMessage,
1600
+ suggestionsHeight,
1601
+ messageHeight,
1602
+ statusRow,
1603
+ };
1604
+ }
1605
+
1606
+ render() {
1607
+ if (!this.running) return;
1608
+ const tab = this.tab;
1609
+ const {
1610
+ tabBarHeight,
1611
+ keymenuHeight,
1612
+ activeSuggestions,
1613
+ activeSuggestionIdx,
1614
+ activeMessage,
1615
+ suggestionsHeight,
1616
+ messageHeight,
1617
+ statusRow,
1618
+ } = this.layoutEditorArea();
1619
+
1520
1620
  const defaultStyle = this.context.colorscheme?.defaultStyle ?? {};
1521
1621
  this.screen.fill(" ", defaultStyle);
1522
1622
 
1523
1623
  this.tabRects = [];
1524
1624
  if (tabBarHeight) this.renderTabbar(defaultStyle);
1525
1625
 
1526
- // Compute pane rects for this tab
1527
- computeLayout(tab.root, 0, editorAreaTop, this.cols, editorAreaH);
1528
-
1529
1626
  // Center scroll for any buffer restored from savecursor (deferred until layout is known)
1530
1627
  for (const p of tab.panes()) {
1531
1628
  if (p.buffer?._pendingCenterScroll) {
@@ -1649,8 +1746,9 @@ class App {
1649
1746
  if (softwrap) {
1650
1747
  const scrollSloc = { line: buf.scroll.y, row: buf.scroll.row ?? 0 };
1651
1748
  const cursorLine = buf.lines[buf.cursor.y] ?? "";
1749
+ const cursorX = normalizeCharBoundary(cursorLine, buf.cursor.x);
1652
1750
  const cursorBreaks = softwrapBreaks(cursorLine, bufW, wordwrap, tabsize);
1653
- const cursorSubRow = softwrapRowOfCharIdx(cursorBreaks, buf.cursor.x);
1751
+ const cursorSubRow = softwrapRowOfCharIdx(cursorBreaks, cursorX);
1654
1752
  const cursorSloc = { line: buf.cursor.y, row: cursorSubRow };
1655
1753
  const cursorAbove = cursorSloc.line < scrollSloc.line ||
1656
1754
  (cursorSloc.line === scrollSloc.line && cursorSloc.row < scrollSloc.row);
@@ -1659,10 +1757,12 @@ class App {
1659
1757
  : slocDiff(buf.lines, scrollSloc, cursorSloc, bufW, wordwrap, tabsize);
1660
1758
  cursorRow = p.y + visualRowOffset;
1661
1759
  const segStart = cursorBreaks[cursorSubRow] ?? 0;
1662
- cursorCol = p.x + gutterW + displayWidth(cursorLine.slice(segStart, buf.cursor.x));
1760
+ cursorCol = p.x + gutterW + displayWidth(cursorLine.slice(segStart, cursorX));
1663
1761
  } else {
1762
+ const line = buf.line();
1763
+ const cursorX = normalizeCharBoundary(line, buf.cursor.x);
1664
1764
  cursorRow = p.y + buf.cursor.y - buf.scroll.y;
1665
- cursorCol = p.x + gutterW + displayWidth(buf.line().slice(buf.scroll.x, buf.cursor.x));
1765
+ cursorCol = p.x + gutterW + displayWidth(line.slice(buf.scroll.x, cursorX));
1666
1766
  }
1667
1767
 
1668
1768
  const cursorVisible = cursorRow >= p.y && cursorRow < p.y + p.h && cursorCol >= p.x && cursorCol < p.x + p.w;
@@ -1891,11 +1991,16 @@ class App {
1891
1991
  for (let col = 0; col < Math.min(pane.w, vt.cols); col++) {
1892
1992
  const cell = vtRow[col];
1893
1993
  if (!cell) continue;
1894
- this.screen.setContent(pane.x + col, pane.y + 1 + row, cell.ch || " ", {
1994
+ const style = {
1895
1995
  fg: cell.fg, bg: cell.bg,
1896
1996
  bold: cell.bold, italic: cell.italic,
1897
1997
  underline: cell.underline, reverse: cell.reverse,
1898
- });
1998
+ };
1999
+ if (cell.filler) {
2000
+ this.screen.setFillerContent(pane.x + col, pane.y + 1 + row, style);
2001
+ continue;
2002
+ }
2003
+ this.screen.setContent(pane.x + col, pane.y + 1 + row, cell.ch || " ", style, cell.combining ?? []);
1899
2004
  }
1900
2005
  }
1901
2006
  // Show VT cursor only when live (not scrolled back) and active
@@ -2316,6 +2421,7 @@ class App {
2316
2421
  switch (seq) {
2317
2422
  case "escape": {
2318
2423
  this.pane.selection = null;
2424
+ this._markSelStart = null;
2319
2425
  if (buf) buf.searchPattern = "";
2320
2426
  const count = forceRehighlightDirtyLongLines(buf, this);
2321
2427
  if (count > 0) this.message = `Rehighlighted ${count} long line${count === 1 ? "" : "s"}`;
@@ -2620,6 +2726,20 @@ class App {
2620
2726
  case "alt-down":
2621
2727
  await runAction("MoveLinesDown", this);
2622
2728
  break;
2729
+ case "alt-d": //DedentSelection
2730
+ await runAction("OutdentSelection", this);
2731
+ break;
2732
+ case "alt-s": { //Mark selection start / extend selection to mark
2733
+ if (!this._markSelStart) {
2734
+ this._markSelStart = { ...buf.cursor };
2735
+ this.message = "selectionStart, ESC:cancel";
2736
+ } else {
2737
+ this.pane.selection = { start: { ...this._markSelStart }, end: { ...buf.cursor } };
2738
+ buf.cursor = { ...buf.cursor };
2739
+ this.message = "selectionEnd, ESC:cancel";
2740
+ }
2741
+ break;
2742
+ }
2623
2743
  case "alt-p": //PreviousTab
2624
2744
  await runAction("PreviousTab", this);
2625
2745
  break;
@@ -5285,8 +5405,11 @@ function renderHighlightedCells(buf, lineNo, scrollX, maxWidth, colorscheme, sel
5285
5405
  let i = scrollX;
5286
5406
  while (i < raw.length && width < maxWidth) {
5287
5407
  const cp = raw.codePointAt(i);
5288
- const ch = String.fromCodePoint(cp);
5289
- const charLen = cp > 0xFFFF ? 2 : 1;
5408
+ const unit = displayUnitAt(raw, i);
5409
+ const ch = unit.text;
5410
+ const charLen = unit.length;
5411
+ const w = unit.width;
5412
+ if (charLen <= 0) break;
5290
5413
  while (changeIndex + 1 < changes.length && i >= changes[changeIndex + 1][0]) changeIndex++;
5291
5414
  const group = changes[changeIndex]?.[1] ?? "default";
5292
5415
  const syntaxStyle = colorscheme?.get(group) ?? colorscheme?.defaultStyle ?? {};
@@ -5311,13 +5434,12 @@ function renderHighlightedCells(buf, lineNo, scrollX, maxWidth, colorscheme, sel
5311
5434
  if (selected) {
5312
5435
  style = { ...style, reverse: !style.reverse };
5313
5436
  }
5314
- const w = charWidth(ch);
5315
5437
  if (ch === "\t") {
5316
5438
  const spaces = Math.min(DEFAULT_SETTINGS.tabsize, maxWidth - width);
5317
5439
  for (let j = 0; j < spaces; j++) cells.push({ ch: " ", style });
5318
5440
  width += spaces;
5319
5441
  } else if (w > 0 && width + w <= maxWidth) {
5320
- cells.push({ ch, style, wide: w === 2 });
5442
+ cells.push({ ch, style, width: w });
5321
5443
  width += w;
5322
5444
  }
5323
5445
  i += charLen;
@@ -5333,20 +5455,29 @@ function renderHighlightedCells(buf, lineNo, scrollX, maxWidth, colorscheme, sel
5333
5455
  function putText(screen, x, y, text, style = null, maxWidth = Infinity) {
5334
5456
  let col = x;
5335
5457
  let width = 0;
5336
- for (const ch of String(text)) {
5458
+ const str = String(text);
5459
+ for (let i = 0; i < str.length;) {
5337
5460
  if (width >= maxWidth) break;
5461
+ const unit = displayUnitAt(str, i);
5462
+ const ch = unit.text;
5463
+ const w = unit.width;
5464
+ if (unit.length <= 0) break;
5338
5465
  if (ch === "\t") {
5339
5466
  const spaces = Math.min(DEFAULT_SETTINGS.tabsize, maxWidth - width);
5340
5467
  for (let i = 0; i < spaces; i++) screen.setContent(col++, y, " ", style);
5341
5468
  width += spaces;
5469
+ i += unit.length;
5470
+ continue;
5471
+ }
5472
+ if (w <= 0 || width + w > maxWidth) {
5473
+ i += unit.length;
5342
5474
  continue;
5343
5475
  }
5344
- const w = charWidth(ch);
5345
- if (w <= 0 || width + w > maxWidth) continue;
5346
5476
  screen.setContent(col, y, ch, style);
5347
5477
  if (w === 2) screen.setFillerContent(col + 1, y, style);
5348
5478
  col += w;
5349
5479
  width += w;
5480
+ i += unit.length;
5350
5481
  }
5351
5482
  return col;
5352
5483
  }
@@ -5356,7 +5487,7 @@ function putCells(screen, x, y, cells, maxWidth = Infinity) {
5356
5487
  let width = 0;
5357
5488
  for (const cell of cells) {
5358
5489
  if (width >= maxWidth) break;
5359
- const w = charWidth(cell.ch);
5490
+ const w = cell.width ?? charWidth(cell.ch);
5360
5491
  if (w <= 0 || width + w > maxWidth) continue;
5361
5492
  screen.setContent(col, y, cell.ch, cell.style);
5362
5493
  if (w === 2) screen.setFillerContent(col + 1, y, cell.style);
@@ -180,6 +180,11 @@ function registerBuiltinActions() {
180
180
  buf.modified = true;
181
181
  }
182
182
  });
183
+ // Aliases for OutdentSelection / OutdentLine
184
+ reg("DedentSelection", (app) => ACTIONS.get("OutdentSelection")(app));
185
+ reg("UnindentSelection", (app) => ACTIONS.get("OutdentSelection")(app));
186
+ reg("DedentLine", (app) => ACTIONS.get("OutdentLine")(app));
187
+ reg("UnindentLine", (app) => ACTIONS.get("OutdentLine")(app));
183
188
 
184
189
  // Editing
185
190
  reg("Backspace", (app) => app.buffer?.backspace());
@@ -59,7 +59,19 @@ export class Screen {
59
59
  let out = "\x1b[?25l";
60
60
  let activeStyleKey = null;
61
61
  for (const { x, y, cell } of changes) {
62
- if (cell.filler) continue; // right-half of a wide char; the char itself already covers this column
62
+ if (cell.filler) continue; // right-half of a wide char; the base cell covers this column
63
+ // If this is a wide char (next cell is its filler), clear the filler column with
64
+ // default style first. On narrow-emoji terminals this leaves a default-bg blank
65
+ // at the right-half column instead of stale cursor-line / syntax background, so
66
+ // the area next to the glyph doesn't look like a colored block "covering" it.
67
+ // On wide-emoji terminals the glyph's right half overwrites the blank harmlessly.
68
+ const nextCell = this.cells.getContent(x + 1, y);
69
+ if (nextCell?.filler) {
70
+ out += this.move(y + 1, x + 2);
71
+ out += styleToAnsi({});
72
+ activeStyleKey = "";
73
+ out += " ";
74
+ }
63
75
  out += this.move(y + 1, x + 1);
64
76
  if (cell.styleKey !== activeStyleKey) {
65
77
  out += styleToAnsi(cell.style ?? {});