swift-displaywidth

0.1.0

wcwidth implementation with the latest Unicode spec
ainame/swift-displaywidth

What's New

0.1.0

2026-04-06T19:51:52Z

Added

  • Added stripsANSI so string measurement can ignore CSI, OSC, and APC/Kitty-style escape sequences without changing character or scalar width behavior. #1
  • Added tab-aware string measurement for terminal-style tab stops and fixed-space tab expansion through DisplayWidth.Tab. #3
  • Added regression coverage for embedded terminal image escapes and the tab mode behavior differences. #1

Changed

  • Replaced the older tabWidth configuration with the clearer tab: DisplayWidth.Tab API, including .tabStops(n) and .fixedSpaces(n). #3
  • Improved the README with ANSI-aware examples, tab mode diagrams, and guidance on how tab-stop and fixed-space counting differ. #1
  • Reduced overhead in processed string measurement by scanning Unicode scalars in chunks and avoiding temporary array allocation for multi-scalar graphemes. #2

Fixed

  • Fixed follow-up issues in the ANSI and tab-aware measurement work while preserving the default behavior when the new options are left unset. #1

swift-displaywidth

Swift Version Swift Package Manager License GitHub Release Build Status

A portable/cross-platform implementation of wcwidth(3) with up-to-date Unicode spec. This project has own Unicode data tables generated from following files.

Why use this?

Instead of this library, there's wcwidth imported with import Darwin, import Musl, or import Glibc.

If that meets your requirements, you should just use it. However, this project has following superior points.

  • Portable/Cross-platform implementation that doesn't require a C library nor even Foundation
  • Up-to-date Unicode spec
  • Better support of Unicode grapheme clusters
  • Swift-friendly API

Usage

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/ainame/swift-displaywidth", from: "0.1.0")
]

Then:

import DisplayWidth

// call as function
let baseDisplayWidth = DisplayWidth()
baseDisplayWidth("A")        // 1
baseDisplayWidth("")       // 2
baseDisplayWidth("👩‍💻")       // 2
baseDisplayWidth("e\u{0301}") // 1 (e + combining acute)

// If your environment treat ambiguous chars as full-width,
// you can set this option.
let ambiguousWidth = DisplayWidth(treatAmbiguousAsFullWidth: true)
import DisplayWidth

// ANSI escape sequences can be ignored during string measurement.
let ansiAwareDisplayWidth = DisplayWidth(stripsANSI: true)
ansiAwareDisplayWidth("\u{001B}[31mhello\u{001B}[0m") // 5

// Tabs can advance to the next tab stop every 4 columns.
let tabStopsDisplayWidth = DisplayWidth(tab: .tabStops(4))
tabStopsDisplayWidth("a\tb") // 5

// Or each tab can count as a fixed number of spaces.
let fixedTabsDisplayWidth = DisplayWidth(tab: .fixedSpaces(3))
fixedTabsDisplayWidth("a\tb") // 5

stripsANSI and tab affect string measurement only. Character and scalar measurement keep their existing behavior. DisplayWidth.Tab.tabStops(n) uses terminal-style tab stops, while .fixedSpaces(n) counts each tab as exactly n columns. For example, with 3, "a\tb" is width 4 with .tabStops(3) and width 5 with .fixedSpaces(3).

Diagram with 4:

.tabStops(4)
tab stops: 4, 8, 12, ...

"a\tb"    = 1 + 3 + 1 = 5
"ab\tb"   = 2 + 2 + 1 = 5
"abcd\tb" = 4 + 4 + 1 = 9

.fixedSpaces(4)
every tab = 4

"a\tb"    = 1 + 4 + 1 = 6
"ab\tb"   = 2 + 4 + 1 = 7
"abcd\tb" = 4 + 4 + 1 = 9

Reference: Tab stop

Links

Description

  • Swift Tools 6.0.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sat Apr 25 2026 04:56:34 GMT-0900 (Hawaii-Aleutian Daylight Time)