Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e001d8769 | |||
| 2f34976f3e | |||
| d1208f7e39 | |||
| d6ba875297 | |||
| 529d09aabc | |||
| 5291e7dee6 | |||
| 3c52ade946 | |||
| 0f692e4372 |
@@ -5,5 +5,3 @@
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
.cargo/
|
.cargo/
|
||||||
.sqlx/
|
.sqlx/
|
||||||
|
|
||||||
docker-compose*
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://tools.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
|
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".ChatApplication"
|
android:name=".ChatApplication"
|
||||||
@@ -22,7 +19,6 @@
|
|||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".core.service.MessageStreamService"
|
android:name=".core.service.MessageStreamService"
|
||||||
android:foregroundServiceType="dataSync"
|
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package dev.zxq5.chatapp.android
|
package dev.zxq5.chatapp.android
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -62,14 +66,37 @@ class MainActivity : ComponentActivity() {
|
|||||||
val currentScreen by chatViewModel.currentScreen.collectAsState()
|
val currentScreen by chatViewModel.currentScreen.collectAsState()
|
||||||
val selectedChannelId by chatViewModel.channelId.collectAsState()
|
val selectedChannelId by chatViewModel.channelId.collectAsState()
|
||||||
|
|
||||||
|
// Permission request launcher
|
||||||
|
val launcher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.RequestPermission(),
|
||||||
|
onResult = { isGranted ->
|
||||||
|
if (isGranted && authState == AuthState.Authenticated) {
|
||||||
|
MessageStreamService.start(this@MainActivity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(authState) {
|
LaunchedEffect(authState) {
|
||||||
when (authState) {
|
when (authState) {
|
||||||
AuthState.Authenticated -> MessageStreamService.start(this@MainActivity)
|
AuthState.Authenticated -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
}
|
||||||
|
MessageStreamService.start(this@MainActivity)
|
||||||
|
chatViewModel.loadAccessibleChannels()
|
||||||
|
}
|
||||||
AuthState.Unauthenticated -> MessageStreamService.stop(this@MainActivity)
|
AuthState.Unauthenticated -> MessageStreamService.stop(this@MainActivity)
|
||||||
AuthState.AwaitingTotp -> {}
|
AuthState.AwaitingTotp -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
chatViewModel.onUnauthorized = {
|
||||||
|
authViewModel.logout()
|
||||||
|
chatViewModel.clearChat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
intent.getIntExtra("channel_id", -1).takeIf { it != -1 }?.let {
|
intent.getIntExtra("channel_id", -1).takeIf { it != -1 }?.let {
|
||||||
chatViewModel.switchChannel(it.toLong())
|
chatViewModel.switchChannel(it.toLong())
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
package dev.zxq5.chatapp.android.api
|
package dev.zxq5.chatapp.android.api
|
||||||
|
|
||||||
import dev.zxq5.chatapp.android.BuildConfig.BASE_URL
|
import dev.zxq5.chatapp.android.BuildConfig.BASE_URL
|
||||||
import dev.zxq5.chatapp.android.api.model.Message
|
import dev.zxq5.chatapp.android.api.model.ChatEvent
|
||||||
import dev.zxq5.chatapp.android.api.model.SendMessage
|
import dev.zxq5.chatapp.android.api.model.SendMessage
|
||||||
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
@@ -25,6 +27,8 @@ import kotlinx.coroutines.flow.flow
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
class ChatClient(private val token: String) {
|
class ChatClient(private val token: String) {
|
||||||
|
|
||||||
@@ -45,18 +49,18 @@ class ChatClient(private val token: String) {
|
|||||||
suspend fun sendMessage(channelId: Long, userId: Int, text: String) {
|
suspend fun sendMessage(channelId: Long, userId: Int, text: String) {
|
||||||
http.post("${BASE_URL}/api/chat/$channelId") {
|
http.post("${BASE_URL}/api/chat/$channelId") {
|
||||||
contentType(ContentType.Application.Json)
|
contentType(ContentType.Application.Json)
|
||||||
setBody(SendMessage(user_id = userId, text = text, timestamp = Clock.System.now()))
|
setBody(SendMessage(id = Uuid.random(), user_id = userId, text = text, timestamp = Clock.System.now()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun messageStream(channelId: Long): Flow<Message> = flow {
|
fun eventStream(channelId: Long): Flow<ChatEvent> = flow {
|
||||||
http.prepareGet("${BASE_URL}/api/events/$channelId").execute { response ->
|
http.prepareGet("${BASE_URL}/api/events/$channelId").execute { response ->
|
||||||
val channel = response.bodyAsChannel()
|
val channel = response.bodyAsChannel()
|
||||||
while (!channel.isClosedForRead) {
|
while (!channel.isClosedForRead) {
|
||||||
val line = channel.readLine() ?: break
|
val line = channel.readLine() ?: break
|
||||||
if (line.startsWith("data:")) {
|
if (line.startsWith("data:")) {
|
||||||
val json = line.removePrefix("data:").trim()
|
val json = line.removePrefix("data:").trim()
|
||||||
runCatching { Json.decodeFromString<Message>(json) }
|
runCatching { Json.decodeFromString<ChatEvent>(json) }
|
||||||
.onSuccess { emit(it) }
|
.onSuccess { emit(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.util.Log
|
|||||||
import dev.zxq5.chatapp.android.BuildConfig.BASE_URL
|
import dev.zxq5.chatapp.android.BuildConfig.BASE_URL
|
||||||
import dev.zxq5.chatapp.android.api.model.AccountDeleteRequest
|
import dev.zxq5.chatapp.android.api.model.AccountDeleteRequest
|
||||||
import dev.zxq5.chatapp.android.api.model.DisplayNameRequest
|
import dev.zxq5.chatapp.android.api.model.DisplayNameRequest
|
||||||
|
import dev.zxq5.chatapp.android.api.model.InviteRequest
|
||||||
import dev.zxq5.chatapp.android.api.model.PasswordChangeRequest
|
import dev.zxq5.chatapp.android.api.model.PasswordChangeRequest
|
||||||
import dev.zxq5.chatapp.android.api.model.QrResponse
|
import dev.zxq5.chatapp.android.api.model.QrResponse
|
||||||
import dev.zxq5.chatapp.android.api.model.TOTPSixDigitCode
|
import dev.zxq5.chatapp.android.api.model.TOTPSixDigitCode
|
||||||
@@ -45,6 +46,22 @@ class SettingsClient(private val token: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun createInvite(request: InviteRequest): ApiResult<String> {
|
||||||
|
return try {
|
||||||
|
val response = http.post("${BASE_URL}/api/invite") {
|
||||||
|
contentType(ContentType.Application.Json)
|
||||||
|
setBody(request)
|
||||||
|
}
|
||||||
|
if (response.status.isSuccess()) {
|
||||||
|
ApiResult.Success(response.body<String>())
|
||||||
|
} else {
|
||||||
|
ApiResult.HttpError(response.status.value, "Failed to create invite")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
ApiResult.NetworkError(e.localizedMessage ?: "Network error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTotpQr(password: String): ApiResult<QrResponse> {
|
suspend fun getTotpQr(password: String): ApiResult<QrResponse> {
|
||||||
return try {
|
return try {
|
||||||
val response = http.post("${BASE_URL}/api/totp.jpg") {
|
val response = http.post("${BASE_URL}/api/totp.jpg") {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class, ExperimentalTime::class)
|
||||||
|
|
||||||
|
package dev.zxq5.chatapp.android.api.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.Instant
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
@Serializable
|
||||||
|
@JsonClassDiscriminator("type")
|
||||||
|
sealed class ChatEvent {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("SendMessage")
|
||||||
|
data class SendMessage(
|
||||||
|
val data: Message
|
||||||
|
) : ChatEvent()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("EditMessage")
|
||||||
|
data class EditMessage(
|
||||||
|
val data: EditMessageContent
|
||||||
|
) : ChatEvent()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("MessageAppendContent")
|
||||||
|
data class MessageAppendContent(
|
||||||
|
val data: AppendContent
|
||||||
|
) : ChatEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// tuple variants like (i64, ChatMsg) and (i64, String)
|
||||||
|
// need wrapper classes since kotlinx can't deserialise
|
||||||
|
// bare JSON arrays into data classes directly
|
||||||
|
@Serializable
|
||||||
|
data class EditMessageContent(
|
||||||
|
val id: Uuid,
|
||||||
|
val message: Message
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AppendContent (
|
||||||
|
val id: Uuid,
|
||||||
|
val content: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Message (
|
||||||
|
val id: Uuid,
|
||||||
|
val user_id: Int,
|
||||||
|
val display_name: String,
|
||||||
|
val text: String,
|
||||||
|
val timestamp: Instant
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.zxq5.chatapp.android.api.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.Instant
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class InviteRequest @OptIn(ExperimentalTime::class) constructor(
|
||||||
|
val name: String,
|
||||||
|
val max_uses: Int,
|
||||||
|
val expiry_date: Instant,
|
||||||
|
val start_date: Instant
|
||||||
|
)
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package dev.zxq5.chatapp.android.model
|
package dev.zxq5.chatapp.android.api.model
|
||||||
|
|
||||||
sealed class LoginState {
|
sealed class LoginState {
|
||||||
object Idle : LoginState()
|
object Idle : LoginState()
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package dev.zxq5.chatapp.android.api.model
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlin.time.ExperimentalTime
|
|
||||||
import kotlin.time.Instant
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Message @OptIn(ExperimentalTime::class) constructor(
|
|
||||||
val user_id: Int,
|
|
||||||
val display_name: String,
|
|
||||||
val text: String,
|
|
||||||
val timestamp: Instant
|
|
||||||
)
|
|
||||||
@@ -3,9 +3,12 @@ package dev.zxq5.chatapp.android.api.model
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.Instant
|
import kotlin.time.Instant
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SendMessage @OptIn(ExperimentalTime::class) constructor(
|
data class SendMessage @OptIn(ExperimentalTime::class, ExperimentalUuidApi::class) constructor(
|
||||||
|
val id: Uuid,
|
||||||
val user_id: Int,
|
val user_id: Int,
|
||||||
val text: String,
|
val text: String,
|
||||||
val timestamp: Instant
|
val timestamp: Instant
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package dev.zxq5.chatapp.android.core.data
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
import androidx.security.crypto.MasterKey
|
import androidx.security.crypto.MasterKey
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
private const val KEY = "auth_token"
|
private const val KEY = "auth_token"
|
||||||
private const val TWOFA_KEY = "twofa_enabled"
|
private const val TWOFA_KEY = "twofa_enabled"
|
||||||
@@ -27,11 +29,37 @@ class TokenStore(appContext: Context) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun save(token: String) =
|
fun save(token: String) {
|
||||||
prefs().edit { putString(KEY, token) }
|
Log.d("TokenStore", "Saving token: $token")
|
||||||
|
|
||||||
|
prefs().edit { putString(KEY, token) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(): String? {
|
||||||
|
val ret = prefs().getString(KEY, null)
|
||||||
|
Log.d("TokenStore", "Retrieved token: $ret")
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isExpired(): Boolean {
|
||||||
|
val token = get() ?: return true
|
||||||
|
return try {
|
||||||
|
val payload = token.split(".")[1]
|
||||||
|
val padded = payload + "==".take((4 - payload.length % 4) % 4)
|
||||||
|
val jsonString = String(Base64.decode(padded, Base64.URL_SAFE))
|
||||||
|
val json = JSONObject(jsonString)
|
||||||
|
if (json.has("exp")) {
|
||||||
|
val exp = json.getLong("exp")
|
||||||
|
val now = Instant.now().epochSecond
|
||||||
|
now >= exp
|
||||||
|
} else {
|
||||||
|
false // If no exp claim, assume not expired or handle differently
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
true // If we can't parse it, treat it as expired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun get(): String? =
|
|
||||||
prefs().getString(KEY, null)
|
|
||||||
|
|
||||||
fun save2faEnabled( enabled: Boolean) =
|
fun save2faEnabled( enabled: Boolean) =
|
||||||
prefs().edit { putBoolean(TWOFA_KEY, enabled) }
|
prefs().edit { putBoolean(TWOFA_KEY, enabled) }
|
||||||
|
|||||||
+17
-29
@@ -3,10 +3,10 @@ package dev.zxq5.chatapp.android.core.service
|
|||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import dev.zxq5.chatapp.android.ChatApplication
|
import dev.zxq5.chatapp.android.ChatApplication
|
||||||
|
import dev.zxq5.chatapp.android.api.model.ChatEvent
|
||||||
import dev.zxq5.chatapp.android.data.repository.ChatRepository
|
import dev.zxq5.chatapp.android.data.repository.ChatRepository
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -15,21 +15,17 @@ import kotlinx.coroutines.cancel
|
|||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
// core/service/MessageStreamService.kt
|
|
||||||
class MessageStreamService : Service() {
|
class MessageStreamService : Service() {
|
||||||
|
|
||||||
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
private lateinit var notificationService: NotificationService
|
private lateinit var notificationService: NotificationService
|
||||||
private lateinit var chatRepository: ChatRepository
|
private lateinit var chatRepository: ChatRepository
|
||||||
|
|
||||||
// which channel the user is currently looking at
|
|
||||||
// set by the ViewModel when the user opens/closes a channel
|
|
||||||
var activeChannelId: Long? = null
|
var activeChannelId: Long? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
Log.d("Service", "activeChannelId set to $value")
|
Log.d("Service", "activeChannelId set to $value")
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
// restart stream with new channel
|
|
||||||
currentStreamJob?.cancel()
|
currentStreamJob?.cancel()
|
||||||
observeMessages()
|
observeMessages()
|
||||||
}
|
}
|
||||||
@@ -42,11 +38,7 @@ class MessageStreamService : Service() {
|
|||||||
|
|
||||||
fun start(context: Context) {
|
fun start(context: Context) {
|
||||||
val intent = Intent(context, MessageStreamService::class.java)
|
val intent = Intent(context, MessageStreamService::class.java)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
context.startService(intent)
|
||||||
context.startForegroundService(intent)
|
|
||||||
} else {
|
|
||||||
context.startService(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stop(context: Context) {
|
fun stop(context: Context) {
|
||||||
@@ -62,33 +54,29 @@ class MessageStreamService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
startForeground(
|
|
||||||
NotificationService.FOREGROUND_NOTIFICATION_ID,
|
|
||||||
notificationService.buildForegroundNotification()
|
|
||||||
)
|
|
||||||
observeMessages()
|
observeMessages()
|
||||||
return START_STICKY // restart if killed
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeMessages() {
|
private fun observeMessages() {
|
||||||
val channelId = activeChannelId ?: chatRepository.getLastActiveChannel()
|
val channelId = activeChannelId ?: chatRepository.getLastActiveChannel()
|
||||||
Log.d("Service", "observeMessages called, channelId=$channelId")
|
if (channelId == null) return
|
||||||
if (channelId == null) {
|
|
||||||
Log.d("Service", "No channel to observe, waiting for switchChannel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("Service", "Starting stream for channel $channelId")
|
|
||||||
currentStreamJob = serviceScope.launch {
|
currentStreamJob = serviceScope.launch {
|
||||||
chatRepository.messageStream(channelId)
|
chatRepository.eventStream(channelId)
|
||||||
.catch { e -> Log.e("Service", "Stream error", e) }
|
.catch { e -> Log.e("Service", "Stream error", e) }
|
||||||
.collect { message ->
|
.collect { event ->
|
||||||
if (!ChatApplication.AppState.isInForeground) { // no channel focused, always notify
|
// Only show notification when an event (new message) is received
|
||||||
notificationService.showMessageNotification(
|
// and the app is not in the foreground on this channel.
|
||||||
conversationId = activeChannelId.toString(),
|
if (!ChatApplication.AppState.isInForeground || activeChannelId != channelId) {
|
||||||
senderName = message.display_name,
|
when (event) {
|
||||||
messagePreview = message.text.take(80)
|
is ChatEvent.SendMessage -> notificationService.showMessageNotification(
|
||||||
)
|
conversationId = channelId.toString(),
|
||||||
|
senderName = event.data.display_name,
|
||||||
|
messagePreview = event.data.text
|
||||||
|
)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-29
@@ -15,51 +15,41 @@ class NotificationService(private val context: Context) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "messages"
|
const val CHANNEL_ID = "messages"
|
||||||
const val FOREGROUND_NOTIFICATION_ID = 1 // ← this needs to exist
|
const val SERVICE_CHANNEL_ID = "service"
|
||||||
|
const val FOREGROUND_NOTIFICATION_ID = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private val manager = context.getSystemService(NotificationManager::class.java)
|
private val manager = context.getSystemService(NotificationManager::class.java)
|
||||||
|
|
||||||
fun createChannels() {
|
fun createForegroundNotification(): Notification {
|
||||||
// channel for new message notifications
|
val intent = Intent(context, MainActivity::class.java).apply {
|
||||||
val messageChannel = NotificationChannel(
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
CHANNEL_ID,
|
|
||||||
"Messages",
|
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
|
||||||
).apply {
|
|
||||||
enableVibration(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel for the persistent foreground service notification
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
// low importance so it doesn't make noise
|
context,
|
||||||
val serviceChannel = NotificationChannel(
|
0,
|
||||||
"service",
|
intent,
|
||||||
"Background connection",
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val mgr = context.getSystemService(NotificationManager::class.java)
|
return NotificationCompat.Builder(context, SERVICE_CHANNEL_ID)
|
||||||
mgr.createNotificationChannel(messageChannel)
|
|
||||||
mgr.createNotificationChannel(serviceChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildForegroundNotification(): Notification {
|
|
||||||
return NotificationCompat.Builder(context, "service")
|
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setContentTitle("chatapp")
|
.setContentTitle("Chat App")
|
||||||
.setContentText("Connected")
|
.setContentText("Connecting to message stream...")
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setSilent(true)
|
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showMessageNotification(
|
fun showMessageNotification(
|
||||||
conversationId: String,
|
conversationId: String,
|
||||||
senderName: String,
|
senderName: String,
|
||||||
messagePreview: String, // for E2E this would be "New message" — no plaintext
|
messagePreview: String,
|
||||||
notificationId: Int = conversationId.hashCode()
|
notificationId: Int = conversationId.hashCode()
|
||||||
) {
|
) {
|
||||||
// intent that opens the app to the right conversation when tapped
|
|
||||||
val intent = Intent(context, MainActivity::class.java).apply {
|
val intent = Intent(context, MainActivity::class.java).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
putExtra("conversation_id", conversationId)
|
putExtra("conversation_id", conversationId)
|
||||||
@@ -72,13 +62,13 @@ class NotificationService(private val context: Context) {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
val notification = NotificationCompat.Builder(context, "messages")
|
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setContentTitle(senderName)
|
.setContentTitle(senderName)
|
||||||
.setContentText(messagePreview)
|
.setContentText(messagePreview)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true) // dismiss on tap
|
.setAutoCancel(true)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
manager.notify(notificationId, notification)
|
manager.notify(notificationId, notification)
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class AuthRepository(
|
|||||||
|
|
||||||
fun getAuthState(): AuthState {
|
fun getAuthState(): AuthState {
|
||||||
val token = tokenStore.get() ?: return AuthState.Unauthenticated
|
val token = tokenStore.get() ?: return AuthState.Unauthenticated
|
||||||
|
if (tokenStore.isExpired()) {
|
||||||
|
tokenStore.clear()
|
||||||
|
return AuthState.Unauthenticated
|
||||||
|
}
|
||||||
return when (getScopeFromToken(token)) {
|
return when (getScopeFromToken(token)) {
|
||||||
TokenScope.FULL -> AuthState.Authenticated
|
TokenScope.FULL -> AuthState.Authenticated
|
||||||
TokenScope.TOTP_PENDING -> AuthState.AwaitingTotp
|
TokenScope.TOTP_PENDING -> AuthState.AwaitingTotp
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
package dev.zxq5.chatapp.android.data.repository
|
package dev.zxq5.chatapp.android.data.repository
|
||||||
|
|
||||||
import dev.zxq5.chatapp.android.api.ChatClient
|
import dev.zxq5.chatapp.android.api.ChatClient
|
||||||
|
import dev.zxq5.chatapp.android.api.model.ChatEvent
|
||||||
import dev.zxq5.chatapp.android.core.data.TokenStore
|
import dev.zxq5.chatapp.android.core.data.TokenStore
|
||||||
import dev.zxq5.chatapp.android.api.model.Message
|
import dev.zxq5.chatapp.android.api.model.Message
|
||||||
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
||||||
@@ -43,8 +44,8 @@ class ChatRepository(private val tokenStore: TokenStore) {
|
|||||||
getChatClient()?.sendMessage(channelId, userId, text)
|
getChatClient()?.sendMessage(channelId, userId, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun messageStream(channelId: Long): Flow<Message> {
|
fun eventStream(channelId: Long): Flow<ChatEvent> {
|
||||||
_lastActiveChannel = channelId
|
_lastActiveChannel = channelId
|
||||||
return getChatClient()?.messageStream(channelId) ?: emptyFlow()
|
return getChatClient()?.eventStream(channelId) ?: emptyFlow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -2,6 +2,7 @@ package dev.zxq5.chatapp.android.data.repository
|
|||||||
|
|
||||||
import dev.zxq5.chatapp.android.api.model.QrResponse
|
import dev.zxq5.chatapp.android.api.model.QrResponse
|
||||||
import dev.zxq5.chatapp.android.api.SettingsClient
|
import dev.zxq5.chatapp.android.api.SettingsClient
|
||||||
|
import dev.zxq5.chatapp.android.api.model.InviteRequest
|
||||||
import dev.zxq5.chatapp.android.api.model.TotpStatus
|
import dev.zxq5.chatapp.android.api.model.TotpStatus
|
||||||
import dev.zxq5.chatapp.android.core.data.TokenStore
|
import dev.zxq5.chatapp.android.core.data.TokenStore
|
||||||
import dev.zxq5.chatapp.android.core.error.ApiResult
|
import dev.zxq5.chatapp.android.core.error.ApiResult
|
||||||
@@ -25,6 +26,10 @@ class SettingsRepository(private val tokenStore: TokenStore) {
|
|||||||
_lastToken = null
|
_lastToken = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun createInvite(request: InviteRequest): ApiResult<String> {
|
||||||
|
return getSettingsClient()?.createInvite(request) ?: ApiResult.NetworkError("Not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTotpQr(password: String): ApiResult<QrResponse?> {
|
suspend fun getTotpQr(password: String): ApiResult<QrResponse?> {
|
||||||
val settingsClient = getSettingsClient() ?: return ApiResult.NetworkError("Not authenticated")
|
val settingsClient = getSettingsClient() ?: return ApiResult.NetworkError("Not authenticated")
|
||||||
return settingsClient.getTotpQr(password)
|
return settingsClient.getTotpQr(password)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package dev.zxq5.chatapp.android.feature.auth
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import dev.zxq5.chatapp.android.model.LoginState
|
import dev.zxq5.chatapp.android.api.model.LoginState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AuthScreen(viewModel: AuthViewModel) {
|
fun AuthScreen(viewModel: AuthViewModel) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import dev.zxq5.chatapp.android.data.repository.AuthRepository
|
|||||||
import dev.zxq5.chatapp.android.data.repository.LoginResult
|
import dev.zxq5.chatapp.android.data.repository.LoginResult
|
||||||
import dev.zxq5.chatapp.android.data.repository.SignupResult
|
import dev.zxq5.chatapp.android.data.repository.SignupResult
|
||||||
import dev.zxq5.chatapp.android.data.repository.AuthState
|
import dev.zxq5.chatapp.android.data.repository.AuthState
|
||||||
import dev.zxq5.chatapp.android.model.LoginState
|
import dev.zxq5.chatapp.android.api.model.LoginState
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.zxq5.chatapp.android.model.LoginState
|
import dev.zxq5.chatapp.android.api.model.LoginState
|
||||||
import dev.zxq5.chatapp.android.ui.components.TextField
|
import dev.zxq5.chatapp.android.ui.components.TextField
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.zxq5.chatapp.android.model.LoginState
|
import dev.zxq5.chatapp.android.api.model.LoginState
|
||||||
import dev.zxq5.chatapp.android.ui.components.TextField
|
import dev.zxq5.chatapp.android.ui.components.TextField
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
package dev.zxq5.chatapp.android.feature.chat
|
package dev.zxq5.chatapp.android.feature.chat
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dev.zxq5.chatapp.android.api.model.Channel
|
import dev.zxq5.chatapp.android.api.model.ChatEvent
|
||||||
import dev.zxq5.chatapp.android.data.repository.ChatRepository
|
import dev.zxq5.chatapp.android.data.repository.ChatRepository
|
||||||
import dev.zxq5.chatapp.android.api.model.Message
|
import dev.zxq5.chatapp.android.api.model.Message
|
||||||
import dev.zxq5.chatapp.android.api.model.Space
|
|
||||||
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
import dev.zxq5.chatapp.android.api.model.SpaceDto
|
||||||
import dev.zxq5.chatapp.android.core.service.MessageStreamService
|
import dev.zxq5.chatapp.android.core.service.MessageStreamService
|
||||||
|
import io.ktor.client.plugins.ResponseException
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
||||||
|
|
||||||
@@ -41,6 +46,8 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
|
|
||||||
private var streamJob: Job? = null
|
private var streamJob: Job? = null
|
||||||
|
|
||||||
|
var onUnauthorized: (() -> Unit)? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
_currentUserId.value = chatRepository.getUserId()
|
_currentUserId.value = chatRepository.getUserId()
|
||||||
observeChannel()
|
observeChannel()
|
||||||
@@ -49,6 +56,7 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
|
|
||||||
fun loadAccessibleChannels() {
|
fun loadAccessibleChannels() {
|
||||||
_error.value = null
|
_error.value = null
|
||||||
|
_currentUserId.value = chatRepository.getUserId()
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
chatRepository.getAccessibleChannels()
|
chatRepository.getAccessibleChannels()
|
||||||
@@ -56,11 +64,16 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
_spaces.value = data
|
_spaces.value = data
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
Log.e("Chat", "Failed to load spaces", e)
|
Log.e("Chat", "Failed to load spaces", e)
|
||||||
_error.value = "Failed to load channels: ${e.message}"
|
if (e is ResponseException && e.response.status == HttpStatusCode.Unauthorized) {
|
||||||
|
onUnauthorized?.invoke()
|
||||||
|
} else {
|
||||||
|
_error.value = "Failed to load channels: ${e.message}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
private fun observeChannel() {
|
private fun observeChannel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_channelId.collect { id ->
|
_channelId.collect { id ->
|
||||||
@@ -69,13 +82,40 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
_channelError.value = null
|
_channelError.value = null
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
streamJob = launch {
|
streamJob = launch {
|
||||||
chatRepository.messageStream(id)
|
chatRepository.eventStream(id)
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
Log.e("Chat", "Stream error", e)
|
Log.e("Chat", "Stream error", e)
|
||||||
_channelError.value = "Connection lost: ${e.message}"
|
if (e is ResponseException && e.response.status == HttpStatusCode.Unauthorized) {
|
||||||
|
onUnauthorized?.invoke()
|
||||||
|
} else {
|
||||||
|
_channelError.value = "Connection lost: ${e.message}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.collect { message ->
|
.collect { event ->
|
||||||
_messages.update { it + message }
|
when (event) {
|
||||||
|
is ChatEvent.SendMessage -> {
|
||||||
|
_messages.update { it + event.data }
|
||||||
|
}
|
||||||
|
is ChatEvent.EditMessage -> {
|
||||||
|
_messages.update { messages ->
|
||||||
|
messages.map {
|
||||||
|
if (it.id == event.data.id) event.data.message
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ChatEvent.MessageAppendContent -> {
|
||||||
|
_messages.update { messages ->
|
||||||
|
messages.map { msg ->
|
||||||
|
if (msg.id == event.data.id) {
|
||||||
|
msg.copy(text = msg.text + event.data.content)
|
||||||
|
} else {
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +148,11 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
)
|
)
|
||||||
}.onFailure { e ->
|
}.onFailure { e ->
|
||||||
Log.e("Chat", "Send message error", e)
|
Log.e("Chat", "Send message error", e)
|
||||||
_channelError.value = "Failed to send message"
|
if (e is ResponseException && e.response.status == HttpStatusCode.Unauthorized) {
|
||||||
|
onUnauthorized?.invoke()
|
||||||
|
} else {
|
||||||
|
_channelError.value = "Failed to send message"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,6 +161,7 @@ class ChatViewModel(private val chatRepository: ChatRepository) : ViewModel() {
|
|||||||
_messages.value = emptyList()
|
_messages.value = emptyList()
|
||||||
_channelId.value = null
|
_channelId.value = null
|
||||||
_currentUserId.value = null
|
_currentUserId.value = null
|
||||||
|
_spaces.value = emptyList()
|
||||||
_error.value = null
|
_error.value = null
|
||||||
_channelError.value = null
|
_channelError.value = null
|
||||||
streamJob?.cancel()
|
streamJob?.cancel()
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
package dev.zxq5.chatapp.android.feature.chat
|
package dev.zxq5.chatapp.android.feature.chat
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
@@ -65,6 +67,7 @@ import dev.zxq5.chatapp.android.api.model.Message
|
|||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatScreen(
|
fun ChatScreen(
|
||||||
@@ -277,7 +280,7 @@ fun MessageScreen(channelId: Long, viewModel: ChatViewModel, onBack: () -> Unit)
|
|||||||
modifier = Modifier.weight(1f).padding(horizontal = 16.dp),
|
modifier = Modifier.weight(1f).padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
) {
|
) {
|
||||||
items(messages) { message ->
|
items(messages, key = { it.id }) { message ->
|
||||||
MessageBubble(message, currentUserId)
|
MessageBubble(message, currentUserId)
|
||||||
}
|
}
|
||||||
item { Spacer(Modifier.height(10.dp)) }
|
item { Spacer(Modifier.height(10.dp)) }
|
||||||
@@ -378,7 +381,7 @@ fun MessageBubble(message: Message, currentUserId: Int?) {
|
|||||||
horizontalAlignment = if (isMe) Alignment.End else Alignment.Start
|
horizontalAlignment = if (isMe) Alignment.End else Alignment.Start
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
color = if (isMe) MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.2f),
|
color = if (isMe) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.2f),
|
||||||
shape = RoundedCornerShape(
|
shape = RoundedCornerShape(
|
||||||
topStart = 14.dp,
|
topStart = 14.dp,
|
||||||
topEnd = 14.dp,
|
topEnd = 14.dp,
|
||||||
@@ -388,14 +391,7 @@ fun MessageBubble(message: Message, currentUserId: Int?) {
|
|||||||
border = border(0.5.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
|
border = border(0.5.dp, MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f))
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(horizontal = 11.dp, vertical = 8.dp)) {
|
Column(modifier = Modifier.padding(horizontal = 11.dp, vertical = 8.dp)) {
|
||||||
if (!isMe) {
|
|
||||||
Text(
|
|
||||||
message.display_name?.lowercase() ?: "unknown",
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.7f),
|
|
||||||
modifier = Modifier.padding(bottom = 2.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(
|
Text(
|
||||||
text = message.text,
|
text = message.text,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
@@ -403,10 +399,11 @@ fun MessageBubble(message: Message, currentUserId: Int?) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = time,
|
text = if (!isMe) message.display_name.lowercase() + " . " + time else time,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f),
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
|
||||||
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp)
|
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
@@ -2,6 +2,7 @@ package dev.zxq5.chatapp.android.feature.settings
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dev.zxq5.chatapp.android.api.model.InviteRequest
|
||||||
import dev.zxq5.chatapp.android.api.model.QrResponse
|
import dev.zxq5.chatapp.android.api.model.QrResponse
|
||||||
import dev.zxq5.chatapp.android.core.error.ApiResult
|
import dev.zxq5.chatapp.android.core.error.ApiResult
|
||||||
import dev.zxq5.chatapp.android.data.repository.SettingsRepository
|
import dev.zxq5.chatapp.android.data.repository.SettingsRepository
|
||||||
@@ -27,6 +28,9 @@ class SettingsViewModel(private val settingsRepository: SettingsRepository) : Vi
|
|||||||
private val _isSuccessState = MutableStateFlow<Map<String, Boolean>>(emptyMap())
|
private val _isSuccessState = MutableStateFlow<Map<String, Boolean>>(emptyMap())
|
||||||
val isSuccessState: StateFlow<Map<String, Boolean>> = _isSuccessState
|
val isSuccessState: StateFlow<Map<String, Boolean>> = _isSuccessState
|
||||||
|
|
||||||
|
private val _lastInviteCode = MutableStateFlow<String?>(null)
|
||||||
|
val lastInviteCode: StateFlow<String?> = _lastInviteCode
|
||||||
|
|
||||||
fun clearMessages() {
|
fun clearMessages() {
|
||||||
_settingsError.value = null
|
_settingsError.value = null
|
||||||
_totpError.value = null
|
_totpError.value = null
|
||||||
@@ -40,6 +44,20 @@ class SettingsViewModel(private val settingsRepository: SettingsRepository) : Vi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createInvite(request: InviteRequest) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_settingsError.value = null
|
||||||
|
when (val result = settingsRepository.createInvite(request)) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
_lastInviteCode.value = result.data
|
||||||
|
triggerSuccess("invite")
|
||||||
|
}
|
||||||
|
is ApiResult.HttpError -> _settingsError.value = result.message
|
||||||
|
is ApiResult.NetworkError -> _settingsError.value = result.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun fetchTotpStatus() {
|
fun fetchTotpStatus() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
when (val result = settingsRepository.getTotpStatus()) {
|
when (val result = settingsRepository.getTotpStatus()) {
|
||||||
|
|||||||
@@ -23,11 +23,14 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.ContentCopy
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.DatePicker
|
||||||
|
import androidx.compose.material3.DatePickerDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -37,8 +40,10 @@ import androidx.compose.material3.OutlinedTextField
|
|||||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -57,9 +62,15 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import dev.zxq5.chatapp.android.api.model.InviteRequest
|
||||||
|
import kotlin.time.Duration.Companion.days
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.Instant
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalTime::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
viewModel: SettingsViewModel,
|
viewModel: SettingsViewModel,
|
||||||
@@ -70,6 +81,7 @@ fun SettingsScreen(
|
|||||||
val settingsError by viewModel.settingsError.collectAsState()
|
val settingsError by viewModel.settingsError.collectAsState()
|
||||||
val isSuccessState by viewModel.isSuccessState.collectAsState()
|
val isSuccessState by viewModel.isSuccessState.collectAsState()
|
||||||
val totpError by viewModel.totpError.collectAsState()
|
val totpError by viewModel.totpError.collectAsState()
|
||||||
|
val lastInviteCode by viewModel.lastInviteCode.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.clearMessages()
|
viewModel.clearMessages()
|
||||||
@@ -274,6 +286,120 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsSection(title = "invite") {
|
||||||
|
var inviteName by remember { mutableStateOf("") }
|
||||||
|
var maxUses by remember { mutableStateOf("1") }
|
||||||
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
|
val datePickerState = rememberDatePickerState(
|
||||||
|
initialSelectedDateMillis = System.currentTimeMillis() + 7.days.inWholeMilliseconds
|
||||||
|
)
|
||||||
|
|
||||||
|
Text("create invite token", style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(bottom = 8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = inviteName,
|
||||||
|
onValueChange = { inviteName = it },
|
||||||
|
label = { Text("name") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
OutlinedTextField(
|
||||||
|
value = maxUses,
|
||||||
|
onValueChange = { if (it.all { c -> c.isDigit() }) maxUses = it },
|
||||||
|
label = { Text("max uses") },
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = datePickerState.selectedDateMillis?.let { Instant.fromEpochMilliseconds(it).toString().substringBefore("T") } ?: "",
|
||||||
|
onValueChange = {},
|
||||||
|
label = { Text("expiry date") },
|
||||||
|
readOnly = true,
|
||||||
|
trailingIcon = {
|
||||||
|
IconButton(onClick = { showDatePicker = true }) {
|
||||||
|
Icon(Icons.Default.KeyboardArrowDown, contentDescription = "Select Date")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().clickable { showDatePicker = true },
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (showDatePicker) {
|
||||||
|
DatePickerDialog(
|
||||||
|
onDismissRequest = { showDatePicker = false },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { showDatePicker = false }) {
|
||||||
|
Text("ok")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
DatePicker(state = datePickerState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(12.dp))
|
||||||
|
|
||||||
|
SuccessButton(
|
||||||
|
onClick = {
|
||||||
|
val nowMs = System.currentTimeMillis()
|
||||||
|
val expiryMs = datePickerState.selectedDateMillis ?: (nowMs + 7.days.inWholeMilliseconds)
|
||||||
|
viewModel.createInvite(
|
||||||
|
InviteRequest(
|
||||||
|
name = inviteName,
|
||||||
|
max_uses = maxUses.toIntOrNull() ?: 1,
|
||||||
|
start_date = Instant.fromEpochMilliseconds(nowMs),
|
||||||
|
expiry_date = Instant.fromEpochMilliseconds(expiryMs)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = "generate invite",
|
||||||
|
isSuccess = isSuccessState["invite"] == true,
|
||||||
|
enabled = inviteName.isNotBlank(),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (lastInviteCode != null) {
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f), RoundedCornerShape(8.dp))
|
||||||
|
.padding(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = lastInviteCode!!,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
IconButton(onClick = {
|
||||||
|
clipboardManager.setText(AnnotatedString(lastInviteCode!!))
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Default.ContentCopy, contentDescription = "Copy", modifier = Modifier.size(20.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSection(title = "session") {
|
||||||
|
Button(
|
||||||
|
onClick = onLogout,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = Color.White, contentColor = Color.Black)
|
||||||
|
) {
|
||||||
|
Text("logout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingsSection(title = "danger zone", color = Color.Red.copy(alpha = 0.7f)) {
|
SettingsSection(title = "danger zone", color = Color.Red.copy(alpha = 0.7f)) {
|
||||||
var deletePassword by remember { mutableStateOf("") }
|
var deletePassword by remember { mutableStateOf("") }
|
||||||
var deleteTotp by remember { mutableStateOf("") }
|
var deleteTotp by remember { mutableStateOf("") }
|
||||||
@@ -337,18 +463,6 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsSection(title = "session") {
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
Button(
|
|
||||||
onClick = onLogout,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = Color.White, contentColor = Color.Black)
|
|
||||||
) {
|
|
||||||
Text("logout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsError != null) {
|
if (settingsError != null) {
|
||||||
Text(settingsError!!, color = Color.Red, style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(top = 8.dp))
|
Text(settingsError!!, color = Color.Red, style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(top = 8.dp))
|
||||||
}
|
}
|
||||||
@@ -457,6 +571,7 @@ fun SuccessButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TwoFactorSetup(
|
fun TwoFactorSetup(
|
||||||
qrCodeBase64: String?,
|
qrCodeBase64: String?,
|
||||||
@@ -511,15 +626,13 @@ fun TwoFactorSetup(
|
|||||||
Text(error.lowercase(), color = Color.Red, style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(top = 8.dp))
|
Text(error.lowercase(), color = Color.Red, style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(top = 8.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.height(24.dp))
|
Spacer(Modifier.height(16.dp))
|
||||||
|
SuccessButton(
|
||||||
Button(
|
onClick = { onConfirm(code) },
|
||||||
onClick = { if (code.length == 6) onConfirm(code) },
|
label = "verify and enable",
|
||||||
|
isSuccess = false, // Managed by parent
|
||||||
enabled = code.length == 6,
|
enabled = code.length == 6,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth()
|
||||||
shape = RoundedCornerShape(8.dp)
|
)
|
||||||
) {
|
|
||||||
Text("confirm code")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package dev.zxq5.chatapp.android.ui.components
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@@ -31,6 +33,11 @@ fun TextField(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
textStyle = MaterialTheme.typography.bodyLarge,
|
textStyle = MaterialTheme.typography.bodyLarge,
|
||||||
|
keyboardOptions = if (isPassword) {
|
||||||
|
KeyboardOptions(keyboardType = KeyboardType.Password)
|
||||||
|
} else {
|
||||||
|
KeyboardOptions.Default
|
||||||
|
},
|
||||||
visualTransformation = if (isPassword) PasswordVisualTransformation() else androidx.compose.ui.text.input.VisualTransformation.None,
|
visualTransformation = if (isPassword) PasswordVisualTransformation() else androidx.compose.ui.text.input.VisualTransformation.None,
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
@@ -40,6 +47,6 @@ fun TextField(
|
|||||||
unfocusedBorderColor = MaterialTheme.colorScheme.outline,
|
unfocusedBorderColor = MaterialTheme.colorScheme.outline,
|
||||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
|
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package dev.zxq5.chatapp.android.ui.theme
|
|||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
val Black = Color(0xFF0A0A0A)
|
val Black = Color(0xFF000000)
|
||||||
val DarkGrey = Color(0xFF0D0D0D)
|
val DarkGrey = Color(0xFF0D0D0D)
|
||||||
val Grey = Color(0xFF141414)
|
val Grey = Color(0xFF141414)
|
||||||
val LightGrey = Color(0xFF1E1E1E)
|
val LightGrey = Color(0xFF1E1E1E)
|
||||||
|
|||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="uk.co.ben_gibson.git.link.SettingsState">
|
||||||
|
<option name="host" value="e0f86390-1091-4871-8aeb-f534fbc99cf0" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DataSourcePerFileMappings">
|
<component name="DataSourcePerFileMappings">
|
||||||
<file url="file://$PROJECT_DIR$/sql/schema.sql" value="b14acf5d-6750-469b-8aea-59c8343eb11c" />
|
<file url="file://$PROJECT_DIR$/sql/test.sql" value="b14acf5d-6750-469b-8aea-59c8343eb11c" />
|
||||||
<file url="file://$PROJECT_DIR$/src/repo/user_repo.rs" value="b14acf5d-6750-469b-8aea-59c8343eb11c" />
|
<file url="file://$PROJECT_DIR$/src/repo/user_repo.rs" value="b14acf5d-6750-469b-8aea-59c8343eb11c" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
<file url="file://$PROJECT_DIR$/sql/schema.sql" dialect="PostgreSQL" />
|
<file url="file://$PROJECT_DIR$/migrations/20260412200102_message_id_to_uuid.sql" dialect="PostgreSQL" />
|
||||||
|
<file url="file://$PROJECT_DIR$/sql/test.sql" dialect="PostgreSQL" />
|
||||||
<file url="PROJECT" dialect="PostgreSQL" />
|
<file url="PROJECT" dialect="PostgreSQL" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+3
-3
@@ -12,7 +12,7 @@ image = "0.25.8"
|
|||||||
jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] }
|
jsonwebtoken = { version = "10.3.0", features = ["rust_crypto"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
redis = { version = "0.25.4", features = ["tokio-comp"] }
|
redis = { version = "0.25.4", features = ["tokio-comp"] }
|
||||||
reqwest = { version = "0.12.23", features = ["json"] }
|
reqwest = { version = "0.12.23", features = ["json", "stream"] }
|
||||||
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
rocket = { version = "0.5.1", features = ["json", "secrets"] }
|
||||||
rocket_cors = "0.6.0"
|
rocket_cors = "0.6.0"
|
||||||
rocket_db_pools = { version = "0.2.0", features = ["deadpool_redis", "sqlx_macros", "sqlx_postgres"] }
|
rocket_db_pools = { version = "0.2.0", features = ["deadpool_redis", "sqlx_macros", "sqlx_postgres"] }
|
||||||
@@ -20,11 +20,11 @@ rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
|
|||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.145"
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
sqlx = { version = "0.7.4", features = ["chrono", "macros", "postgres", "time"] }
|
sqlx = { version = "0.7.4", features = ["chrono", "macros", "postgres", "time", "uuid"] }
|
||||||
tokio = { version = "1.47.1", features = ["full"] }
|
tokio = { version = "1.47.1", features = ["full"] }
|
||||||
totp-rs = { version = "5.7.0", features = ["gen_secret", "qr", "rand"] }
|
totp-rs = { version = "5.7.0", features = ["gen_secret", "qr", "rand"] }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
uuid = { version = "1.18.1", features = ["v4"] }
|
uuid = { version = "1.18.1", features = ["serde", "v4"] }
|
||||||
thiserror = "1.0.69"
|
thiserror = "1.0.69"
|
||||||
utoipa = { version = "5.4.0", features = ["rocket_extras", "chrono"] }
|
utoipa = { version = "5.4.0", features = ["rocket_extras", "chrono"] }
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ COPY cdn cdn
|
|||||||
COPY src src
|
COPY src src
|
||||||
COPY Cargo.toml Cargo.toml
|
COPY Cargo.toml Cargo.toml
|
||||||
COPY Rocket.toml Rocket.toml
|
COPY Rocket.toml Rocket.toml
|
||||||
COPY static static
|
|
||||||
COPY templates templates
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y libssl-dev pkg-config
|
RUN apt-get update && apt-get install -y libssl-dev pkg-config
|
||||||
|
|
||||||
@@ -37,9 +35,7 @@ COPY --from=build /build/main ./
|
|||||||
|
|
||||||
## copy runtime assets which may or may not exist
|
## copy runtime assets which may or may not exist
|
||||||
COPY --from=build /build/Rocket.toml ./Rocket.toml
|
COPY --from=build /build/Rocket.toml ./Rocket.toml
|
||||||
COPY --from=build /build/static ./static
|
|
||||||
COPY --from=build /build/cdn ./cdn
|
COPY --from=build /build/cdn ./cdn
|
||||||
COPY --from=build /build/template[s] ./templates
|
|
||||||
|
|
||||||
## ensure the container listens globally on port 8000
|
## ensure the container listens globally on port 8000
|
||||||
ENV ROCKET_ADDRESS=0.0.0.0
|
ENV ROCKET_ADDRESS=0.0.0.0
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[debug]
|
[debug]
|
||||||
secret_key = "yYhvCGnRh/TrcHtB8sZqCFifrVmJxoKFLBYw/WWBZeU="
|
secret_key = "yYhvCGnRh/TrcHtB8sZqCFifrVmJxoKFLBYw/WWBZeU="
|
||||||
address = "0.0.0.0"
|
address = "0.0.0.0"
|
||||||
port = 8000
|
port = 8080
|
||||||
|
|
||||||
[debug.databases.postgres_db]
|
[debug.databases.postgres_db]
|
||||||
url = "postgresql://chatapp:chatapp@100.118.108.58:5432/chatapp_dev"
|
url = "postgresql://chatapp:chatapp@100.118.108.58:5432/chatapp_dev"
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
|
container_name: chatapp_backend
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: .
|
||||||
args:
|
args:
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
environment:
|
env_file:
|
||||||
- ROCKET_SECRET_KEY=${ROCKET_SECRET_KEY}
|
- .env
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
|
||||||
redis:
|
redis:
|
||||||
|
container_name: chatapp_redis
|
||||||
image: docker.io/library/redis:alpine
|
image: docker.io/library/redis:alpine
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "6379:6379"
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
container_name: chatapp_backend
|
container_name: chatapp_backend
|
||||||
image: git.zxq5.dev/zxq5/chatapp-backend:latest
|
image: git.zxq5.dev/zxq5/chatapp-backend:v0.4.1
|
||||||
ports:
|
ports:
|
||||||
- "8080:8000"
|
- "8000:8000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
environment:
|
environment:
|
||||||
- ROCKET_SECRET_KEY=${ROCKET_SECRET_KEY}
|
- RELEASE_MODE=1
|
||||||
- DATABASE_URL=${DATABASE_URL}
|
|
||||||
redis:
|
redis:
|
||||||
container_name: chatapp_redis
|
container_name: chatapp_redis
|
||||||
image: docker.io/library/redis:alpine
|
image: docker.io/library/redis:alpine
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE attachments DROP CONSTRAINT attachments_message_id_fkey;
|
||||||
|
|
||||||
|
ALTER TABLE messages ALTER COLUMN id DROP DEFAULT;
|
||||||
|
ALTER TABLE messages ALTER COLUMN id TYPE uuid USING gen_random_uuid();
|
||||||
|
ALTER TABLE messages ALTER COLUMN id SET DEFAULT gen_random_uuid();
|
||||||
|
|
||||||
|
ALTER TABLE attachments ALTER COLUMN message_id TYPE uuid USING gen_random_uuid();
|
||||||
|
ALTER TABLE attachments ADD CONSTRAINT attachments_message_id_fkey
|
||||||
|
FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
WITH space1 AS (
|
||||||
|
INSERT INTO spaces (name, description, owner_id)
|
||||||
|
VALUES ('general', 'Boring chat idk', 1)
|
||||||
|
RETURNING id
|
||||||
|
),
|
||||||
|
space2 AS (
|
||||||
|
INSERT INTO spaces (name, description, owner_id)
|
||||||
|
VALUES ('Gaming', 'we lose games', 1)
|
||||||
|
RETURNING id
|
||||||
|
)
|
||||||
|
INSERT INTO channels (name, description, space_id)
|
||||||
|
SELECT 'General', 'General chat', id FROM space1 UNION ALL
|
||||||
|
SELECT 'Coding', 'Coding stuff', id FROM space1 UNION ALL
|
||||||
|
SELECT 'AI', '"/ask" here pls :)', id FROM space1 UNION ALL
|
||||||
|
SELECT 'The Game', '(You lost)', id FROM space2 UNION ALL
|
||||||
|
SELECT 'Backrooms', 'Beware of Smilers', id FROM space2 UNION ALL
|
||||||
|
SELECT 'SE', 'Space/Software engineering.', id FROM space2;
|
||||||
+71
-12
@@ -1,7 +1,8 @@
|
|||||||
use crate::error::ApiResult;
|
use crate::error::ApiResult;
|
||||||
use crate::model::auth::{AccessTokenForm, AuthResponse, LoginCredentials, SignupCredentials};
|
use crate::model::auth::{AccessTokenForm, AuthResponse, LoginCredentials, SignupCredentials};
|
||||||
|
use crate::svc::access_token_svc::AccessTokenService;
|
||||||
use crate::svc::auth_svc::AuthService;
|
use crate::svc::auth_svc::AuthService;
|
||||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation, decode, encode};
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::request::{FromRequest, Outcome};
|
use rocket::request::{FromRequest, Outcome};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -9,37 +10,45 @@ use rocket::serde::{Deserialize, Serialize};
|
|||||||
use rocket::{Request, State};
|
use rocket::{Request, State};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use crate::svc::access_token_svc::AccessTokenService;
|
|
||||||
|
|
||||||
#[post("/signup", data = "<cred>")]
|
#[post("/signup", data = "<cred>")]
|
||||||
pub async fn signup(
|
pub async fn signup(
|
||||||
cred: Json<SignupCredentials>,
|
cred: Json<SignupCredentials>,
|
||||||
svc: &State<AuthService>
|
svc: &State<AuthService>,
|
||||||
) -> ApiResult<Json<AuthResponse>> {
|
) -> ApiResult<Json<AuthResponse>> {
|
||||||
let response = svc
|
let response = svc
|
||||||
.signup(
|
.signup(
|
||||||
&cred.email, &cred.username, &cred.password, &cred.access_token,
|
&cred.email,
|
||||||
).await?;
|
&cred.username,
|
||||||
|
&cred.password,
|
||||||
|
&cred.access_token,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(Json(response))
|
Ok(Json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/login", data = "<cred>")]
|
#[post("/login", data = "<cred>")]
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
cred: Json<LoginCredentials>,
|
cred: Json<LoginCredentials>,
|
||||||
svc: &State<AuthService>
|
svc: &State<AuthService>,
|
||||||
) -> ApiResult<Json<AuthResponse>> {
|
) -> ApiResult<Json<AuthResponse>> {
|
||||||
Ok(Json(svc.login(&cred.username, &cred.password).await?))
|
Ok(Json(svc.login(&cred.username, &cred.password).await?))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/invite", data = "<form>")]
|
#[post("/invite", data = "<form>")]
|
||||||
pub async fn generate_invite(
|
pub async fn generate_invite(
|
||||||
session: Session,
|
session: AdminSession,
|
||||||
form: Json<AccessTokenForm>,
|
form: Json<AccessTokenForm>,
|
||||||
svc: &State<AccessTokenService>
|
svc: &State<AccessTokenService>,
|
||||||
) -> ApiResult<String> {
|
) -> ApiResult<String> {
|
||||||
svc.create(
|
svc.create(
|
||||||
session.uid, &form.name, form.max_uses,
|
session.uid,
|
||||||
form.start_date, form.expiry_date).await
|
&form.name,
|
||||||
|
form.max_uses,
|
||||||
|
form.start_date,
|
||||||
|
form.expiry_date,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
static JWT_SECRET: LazyLock<String> = LazyLock::new(|| std::env::var("JWT_SECRET").unwrap());
|
static JWT_SECRET: LazyLock<String> = LazyLock::new(|| std::env::var("JWT_SECRET").unwrap());
|
||||||
@@ -77,6 +86,56 @@ impl<'r> FromRequest<'r> for Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AdminSession {
|
||||||
|
pub uid: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for AdminSession {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||||
|
// First verify the session is valid
|
||||||
|
match Claims::from_request(req).await {
|
||||||
|
Outcome::Success(user) if user.scope == TokenScope::Full => {
|
||||||
|
let uid = user.sub as i64;
|
||||||
|
|
||||||
|
// Get AuthService from Rocket state
|
||||||
|
let auth_svc = match req.guard::<&State<AuthService>>().await {
|
||||||
|
Outcome::Success(svc) => svc,
|
||||||
|
Outcome::Error(err) => {
|
||||||
|
tracing::error!("AdminSession: Failed to get AuthService from state");
|
||||||
|
return Outcome::Error(err);
|
||||||
|
}
|
||||||
|
_ => unreachable!("forward should never be called"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if user is admin
|
||||||
|
match auth_svc.is_admin(uid).await {
|
||||||
|
Ok(true) => Outcome::Success(AdminSession { uid }),
|
||||||
|
Ok(false) => {
|
||||||
|
tracing::debug!("non-admin user attempted to access admin session");
|
||||||
|
Outcome::Error((Status::Forbidden, ()))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!("AdminSession: is_admin check failed: {:?}", err);
|
||||||
|
Outcome::Error((Status::InternalServerError, ()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Outcome::Success(_) => {
|
||||||
|
tracing::debug!("warning: user with scope other than Full attempted to access admin session");
|
||||||
|
Outcome::Error((Status::Forbidden, ()))
|
||||||
|
}
|
||||||
|
Outcome::Error(err) => {
|
||||||
|
tracing::debug!("AdminSession request guard failed: {:?}", err);
|
||||||
|
Outcome::Error(err)
|
||||||
|
}
|
||||||
|
_ => unreachable!("forward should never be called"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Claims {
|
pub struct Claims {
|
||||||
pub sub: i32,
|
pub sub: i32,
|
||||||
@@ -90,9 +149,9 @@ impl Claims {
|
|||||||
sub: user_id as i32,
|
sub: user_id as i32,
|
||||||
exp: (SystemTime::now()
|
exp: (SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.unwrap()
|
.expect("Failed to get time")
|
||||||
.as_secs()
|
.as_secs()
|
||||||
+ 3600) as usize,
|
+ 60 * 60 * 24 * 7) as usize,
|
||||||
scope,
|
scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-16
@@ -9,15 +9,7 @@ use rocket::{Shutdown, State, ___internal_EventStream as EventStream};
|
|||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
use crate::model::event::{ChatEvent, ChatMsg};
|
||||||
/// ---------- Rocket routes ----------
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, FromRow)]
|
|
||||||
pub struct ChatMsg {
|
|
||||||
pub display_name: Option<String>,
|
|
||||||
pub user_id: i64,
|
|
||||||
pub text: String,
|
|
||||||
pub timestamp: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/chat/<channel_id>", format = "json", data = "<msg>")]
|
#[post("/chat/<channel_id>", format = "json", data = "<msg>")]
|
||||||
pub async fn post_message(
|
pub async fn post_message(
|
||||||
@@ -36,24 +28,25 @@ pub async fn event_stream(
|
|||||||
mut shutdown: Shutdown,
|
mut shutdown: Shutdown,
|
||||||
channel_id: i64,
|
channel_id: i64,
|
||||||
) -> ApiResult<EventStream![]> {
|
) -> ApiResult<EventStream![]> {
|
||||||
let messages = chat.get_messages(channel_id, 100)
|
let messages = chat.fetch_latest_messages_desc(channel_id, 100)
|
||||||
.await?; // if get message returned err, inform user.
|
.await?; // if get message returned err, inform user.
|
||||||
|
|
||||||
let mut rx = chat.subscribe(channel_id).await;
|
let mut rx = chat.subscribe(channel_id).await;
|
||||||
let id = s.uid;
|
let id = s.uid;
|
||||||
|
|
||||||
Ok(EventStream! {
|
Ok(EventStream! {
|
||||||
for msg in messages {
|
for msg in messages.into_iter().rev() {
|
||||||
yield Event::json(&msg);
|
// tracing::info!("sending: {:?}", serde_json::to_string(&ChatEvent::SendMessage(msg.clone())).unwrap());
|
||||||
|
yield Event::json(&ChatEvent::SendMessage(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select!{
|
select!{
|
||||||
_ = &mut shutdown => break, // exit early on shutdown
|
_ = &mut shutdown => break, // exit early on shutdown
|
||||||
msg = rx.recv() => match msg {
|
event = rx.recv() => match event {
|
||||||
Ok(msg) => {
|
Ok(event) => {
|
||||||
tracing::info!("yielding message!");
|
// tracing::info!("yielding event: {event:?}");
|
||||||
yield Event::json(&msg)
|
yield Event::json(&event)
|
||||||
},
|
},
|
||||||
Err(broadcast::error::RecvError::Lagged(n)) => {
|
Err(broadcast::error::RecvError::Lagged(n)) => {
|
||||||
tracing::warn!("Receiver lagging on channel {channel_id} by {n} events",);
|
tracing::warn!("Receiver lagging on channel {channel_id} by {n} events",);
|
||||||
|
|||||||
+10
-13
@@ -1,17 +1,15 @@
|
|||||||
use crate::error::ApiResult;
|
|
||||||
use crate::model::space::{Space, SpaceDto};
|
|
||||||
use crate::model::space::Channel;
|
|
||||||
use crate::repo::{SpaceRepo, ChannelRepo};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use rocket::State;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use crate::api::auth::Session;
|
use crate::api::auth::Session;
|
||||||
|
use crate::error::ApiResult;
|
||||||
|
use crate::model::space::Channel;
|
||||||
|
use crate::model::space::{Space, SpaceDto};
|
||||||
|
use crate::repo::{ChannelRepo, SpaceRepo};
|
||||||
use crate::svc::chat_svc::ChatService;
|
use crate::svc::chat_svc::ChatService;
|
||||||
|
use rocket::State;
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[get("/spaces")]
|
#[get("/spaces")]
|
||||||
pub async fn list_spaces(
|
pub async fn list_spaces(space_repo: &State<Arc<dyn SpaceRepo>>) -> ApiResult<Json<Vec<Space>>> {
|
||||||
space_repo: &State<Arc<dyn SpaceRepo>>
|
|
||||||
) -> ApiResult<Json<Vec<Space>>> {
|
|
||||||
let spaces = space_repo.get_all().await?;
|
let spaces = space_repo.get_all().await?;
|
||||||
Ok(Json(spaces))
|
Ok(Json(spaces))
|
||||||
}
|
}
|
||||||
@@ -19,7 +17,7 @@ pub async fn list_spaces(
|
|||||||
#[get("/spaces/<space_id>/channels")]
|
#[get("/spaces/<space_id>/channels")]
|
||||||
pub async fn list_channels(
|
pub async fn list_channels(
|
||||||
space_id: i64,
|
space_id: i64,
|
||||||
channel_repo: &State<Arc<dyn ChannelRepo>>
|
channel_repo: &State<Arc<dyn ChannelRepo>>,
|
||||||
) -> ApiResult<Json<Vec<Channel>>> {
|
) -> ApiResult<Json<Vec<Channel>>> {
|
||||||
let channels = channel_repo.get_by_space_id(space_id).await?;
|
let channels = channel_repo.get_by_space_id(space_id).await?;
|
||||||
Ok(Json(channels))
|
Ok(Json(channels))
|
||||||
@@ -28,9 +26,8 @@ pub async fn list_channels(
|
|||||||
#[get("/accessible_channels")]
|
#[get("/accessible_channels")]
|
||||||
pub async fn get_accessible_channels(
|
pub async fn get_accessible_channels(
|
||||||
session: Session,
|
session: Session,
|
||||||
svc: &State<ChatService>
|
svc: &State<ChatService>,
|
||||||
) -> ApiResult<Json<Vec<SpaceDto>>> {
|
) -> ApiResult<Json<Vec<SpaceDto>>> {
|
||||||
let space = svc.get_accessible_channels(session.uid).await?;
|
let space = svc.get_accessible_channels(session.uid).await?;
|
||||||
println!("{:?}", space);
|
|
||||||
Ok(Json(space))
|
Ok(Json(space))
|
||||||
}
|
}
|
||||||
+47
-53
@@ -4,41 +4,44 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
pub mod messenger;
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod repo;
|
|
||||||
pub mod error;
|
|
||||||
pub mod svc;
|
|
||||||
pub mod model;
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod error;
|
||||||
|
pub mod messenger;
|
||||||
|
pub mod model;
|
||||||
|
pub mod repo;
|
||||||
|
pub mod svc;
|
||||||
|
|
||||||
use crate::repo::{access_token_repo::AccessTokenRepo, Repo};
|
|
||||||
use crate::repo::message_repo::MessageRepository;
|
|
||||||
use crate::repo::user_repo::UserRepository;
|
|
||||||
use crate::repo::space_repo::SpaceRepository;
|
|
||||||
use crate::repo::channel_repo::ChannelRepository;
|
use crate::repo::channel_repo::ChannelRepository;
|
||||||
|
use crate::repo::message_repo::MessageRepository;
|
||||||
|
use crate::repo::space_repo::SpaceRepository;
|
||||||
|
use crate::repo::user_repo::UserRepository;
|
||||||
|
use crate::repo::{Repo, access_token_repo::AccessTokenRepo};
|
||||||
|
use crate::svc::access_token_svc::AccessTokenService;
|
||||||
use crate::svc::auth_svc::AuthService;
|
use crate::svc::auth_svc::AuthService;
|
||||||
use crate::svc::chat_svc::ChatService;
|
use crate::svc::chat_svc::ChatService;
|
||||||
|
use crate::svc::llm_service::LlmService;
|
||||||
use crate::svc::settings_svc::SettingsService;
|
use crate::svc::settings_svc::SettingsService;
|
||||||
use crate::svc::user_svc::UserService;
|
use crate::svc::user_svc::UserService;
|
||||||
use rocket::fs::{FileServer, NamedFile};
|
use api::cdn;
|
||||||
use rocket::http::Method;
|
use rocket::http::Method;
|
||||||
use rocket_cors::{AllowedOrigins, CorsOptions};
|
use rocket_cors::{AllowedOrigins, CorsOptions};
|
||||||
use rocket_dyn_templates::Template;
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use std::env;
|
use std::sync::Arc;
|
||||||
use std::sync::{Arc, LazyLock};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use api::cdn;
|
|
||||||
use crate::svc::access_token_svc::AccessTokenService;
|
|
||||||
use crate::svc::llm_service::LlmService;
|
|
||||||
|
|
||||||
pub fn rocket() -> rocket::Rocket<rocket::Build> {
|
pub fn rocket() -> rocket::Rocket<rocket::Build> {
|
||||||
if std::env::var("RELEASE_MODE").unwrap_or_default() != "1" {
|
|
||||||
dotenv::dotenv().ok();
|
if let Ok(var) = std::env::var("RELEASE_MODE") && var == "1" {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dotenv::dotenv().expect("Failed to load .env file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
|
||||||
|
println!("Running with database URL: {}", db_url);
|
||||||
|
|
||||||
let pool = PgPoolOptions::new()
|
let pool = PgPoolOptions::new()
|
||||||
.max_connections(25)
|
.max_connections(25)
|
||||||
@@ -53,9 +56,22 @@ pub fn rocket() -> rocket::Rocket<rocket::Build> {
|
|||||||
let space_repo: Arc<dyn repo::SpaceRepo> = Arc::new(SpaceRepository::new(pool.clone()));
|
let space_repo: Arc<dyn repo::SpaceRepo> = Arc::new(SpaceRepository::new(pool.clone()));
|
||||||
let channel_repo: Arc<dyn repo::ChannelRepo> = Arc::new(ChannelRepository::new(pool.clone()));
|
let channel_repo: Arc<dyn repo::ChannelRepo> = Arc::new(ChannelRepository::new(pool.clone()));
|
||||||
let llm_service = LlmService::new();
|
let llm_service = LlmService::new();
|
||||||
let chat_service = ChatService::new(32, llm_service.clone(), message_repo.clone(), user_repo.clone(), channel_repo.clone(), space_repo.clone());
|
let chat_service = ChatService::new(
|
||||||
|
32,
|
||||||
|
llm_service.clone(),
|
||||||
|
message_repo.clone(),
|
||||||
|
user_repo.clone(),
|
||||||
|
channel_repo.clone(),
|
||||||
|
space_repo.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
rocket_builder(user_repo, token_repo, space_repo, channel_repo, chat_service)
|
rocket_builder(
|
||||||
|
user_repo,
|
||||||
|
token_repo,
|
||||||
|
space_repo,
|
||||||
|
channel_repo,
|
||||||
|
chat_service,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rocket_builder(
|
pub fn rocket_builder(
|
||||||
@@ -63,10 +79,8 @@ pub fn rocket_builder(
|
|||||||
token_repo: Arc<dyn repo::AccessTokenRepoTrait>,
|
token_repo: Arc<dyn repo::AccessTokenRepoTrait>,
|
||||||
space_repo: Arc<dyn repo::SpaceRepo>,
|
space_repo: Arc<dyn repo::SpaceRepo>,
|
||||||
channel_repo: Arc<dyn repo::ChannelRepo>,
|
channel_repo: Arc<dyn repo::ChannelRepo>,
|
||||||
chat_service: ChatService
|
chat_service: ChatService,
|
||||||
) -> rocket::Rocket<rocket::Build> {
|
) -> rocket::Rocket<rocket::Build> {
|
||||||
|
|
||||||
|
|
||||||
let cors = CorsOptions::default()
|
let cors = CorsOptions::default()
|
||||||
.allowed_origins(AllowedOrigins::all())
|
.allowed_origins(AllowedOrigins::all())
|
||||||
.allowed_methods(
|
.allowed_methods(
|
||||||
@@ -75,7 +89,9 @@ pub fn rocket_builder(
|
|||||||
.map(From::from)
|
.map(From::from)
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.allow_credentials(true);
|
.allow_credentials(true)
|
||||||
|
.to_cors()
|
||||||
|
.expect("unable to create cors");
|
||||||
|
|
||||||
let access_token_svc = AccessTokenService::new(token_repo.clone());
|
let access_token_svc = AccessTokenService::new(token_repo.clone());
|
||||||
let auth_service = AuthService::new(user_repo.clone(), access_token_svc.clone());
|
let auth_service = AuthService::new(user_repo.clone(), access_token_svc.clone());
|
||||||
@@ -86,64 +102,42 @@ pub fn rocket_builder(
|
|||||||
.manage(chat_service)
|
.manage(chat_service)
|
||||||
.manage(auth_service)
|
.manage(auth_service)
|
||||||
.manage(settings_service)
|
.manage(settings_service)
|
||||||
|
.manage(access_token_svc)
|
||||||
.manage(user_service)
|
.manage(user_service)
|
||||||
.manage(space_repo)
|
.manage(space_repo)
|
||||||
.manage(channel_repo)
|
.manage(channel_repo)
|
||||||
.attach(cors.to_cors().unwrap())
|
.attach(cors)
|
||||||
.attach(Template::fairing())
|
|
||||||
.mount("/static", FileServer::from("static"))
|
|
||||||
.mount("/cdn", cdn::routes())
|
|
||||||
.mount(
|
|
||||||
"/",
|
|
||||||
routes![
|
|
||||||
favicon,
|
|
||||||
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.mount(
|
.mount(
|
||||||
"/api",
|
"/api",
|
||||||
routes![
|
routes![
|
||||||
cdn::upload_profile_pic,
|
cdn::upload_profile_pic,
|
||||||
api::profile::display_name,
|
api::profile::display_name,
|
||||||
|
|
||||||
// basic auth
|
// basic auth
|
||||||
api::auth::login,
|
api::auth::login,
|
||||||
api::auth::signup,
|
api::auth::signup,
|
||||||
|
api::auth::generate_invite,
|
||||||
// 2fa
|
// 2fa
|
||||||
api::totp::confirm_totp,
|
api::totp::confirm_totp,
|
||||||
api::totp::disable_totp,
|
api::totp::disable_totp,
|
||||||
api::totp::get_totp,
|
api::totp::get_totp,
|
||||||
api::totp::get_totp_status,
|
api::totp::get_totp_status,
|
||||||
api::totp::verify_totp,
|
api::totp::verify_totp,
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
api::chat::event_stream,
|
api::chat::event_stream,
|
||||||
api::chat::post_message,
|
api::chat::post_message,
|
||||||
|
|
||||||
// user settings
|
// user settings
|
||||||
api::settings::change_display_name,
|
api::settings::change_display_name,
|
||||||
api::settings::change_password,
|
api::settings::change_password,
|
||||||
api::settings::change_username,
|
api::settings::change_username,
|
||||||
api::settings::delete_account,
|
api::settings::delete_account,
|
||||||
|
|
||||||
// spaces
|
// spaces
|
||||||
api::space::list_spaces,
|
api::space::list_spaces,
|
||||||
api::space::list_channels,
|
api::space::list_channels,
|
||||||
api::space::get_accessible_channels
|
api::space::get_accessible_channels
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.register(
|
// .register(
|
||||||
"/",
|
// "/",
|
||||||
catchers![
|
// catchers![error::handle_401, error::handle_404, error::handle_default,],
|
||||||
error::handle_401,
|
// )
|
||||||
error::handle_404,
|
|
||||||
error::handle_default,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/favicon.ico")]
|
|
||||||
pub async fn favicon() -> NamedFile {
|
|
||||||
NamedFile::open("static/favicon.ico").await.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// ---------- Rocket routes ----------
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, FromRow)]
|
||||||
|
pub struct ChatMsg {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
pub user_id: i64,
|
||||||
|
pub text: String,
|
||||||
|
pub timestamp: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type", content = "data")]
|
||||||
|
pub enum ChatEvent {
|
||||||
|
SendMessage(ChatMsg),
|
||||||
|
|
||||||
|
/// for when a user explicitly edits a message
|
||||||
|
EditMessage { id: Uuid, msg: ChatMsg },
|
||||||
|
|
||||||
|
/// used for streaming content to a message
|
||||||
|
/// will not show up as edited
|
||||||
|
MessageAppendContent{ id: Uuid, content: String }
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod space;
|
pub mod space;
|
||||||
|
pub mod event;
|
||||||
@@ -2,6 +2,7 @@ use crate::api::auth::Session;
|
|||||||
use crate::error::ApiResult;
|
use crate::error::ApiResult;
|
||||||
use crate::svc::user_svc::UserService;
|
use crate::svc::user_svc::UserService;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
use crate::api::totp::TotpStatus;
|
use crate::api::totp::TotpStatus;
|
||||||
@@ -20,6 +21,14 @@ pub struct User {
|
|||||||
pub updated_at: Option<DateTime<Utc>>,
|
pub updated_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, sqlx::Type, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[sqlx(type_name = "user_role", rename_all = "lowercase")]
|
||||||
|
pub enum UserRole {
|
||||||
|
User,
|
||||||
|
Admin,
|
||||||
|
}
|
||||||
|
|
||||||
// pub struct UserCache {}
|
// pub struct UserCache {}
|
||||||
//
|
//
|
||||||
// impl UserCache {
|
// impl UserCache {
|
||||||
|
|||||||
@@ -1,68 +1,80 @@
|
|||||||
use crate::api::chat::ChatMsg;
|
use crate::model::event::ChatMsg;
|
||||||
use crate::repo::Repo;
|
use crate::repo::Repo;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MessageRepository {
|
pub struct MessageRepository {
|
||||||
pool: PgPool
|
pool: PgPool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repo for MessageRepository {
|
impl MessageRepository {
|
||||||
type Target = ChatMsg;
|
pub(crate) fn new(pool: PgPool) -> Self {
|
||||||
|
|
||||||
fn new(pool: PgPool) -> Self {
|
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: caching with redis
|
// TODO: caching with redis
|
||||||
async fn get_by_id(&self, id: i64) -> Option<Self::Target> {
|
async fn get_by_id(&self, id: Uuid) -> Option<ChatMsg> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT u.username, u.nickname, u.id as user_id, m.content, m.created_at
|
"SELECT m.id, u.username, u.nickname, u.id as user_id, m.content, m.created_at
|
||||||
FROM messages m
|
FROM messages m
|
||||||
JOIN users u ON m.user_id = u.id
|
JOIN users u ON m.user_id = u.id
|
||||||
WHERE m.id = $1",
|
WHERE m.id = $1",
|
||||||
id
|
id
|
||||||
).fetch_optional(&self.pool).await.ok().flatten().map(|row| ChatMsg {
|
).fetch_optional(&self.pool).await.ok().flatten().map(|row| ChatMsg {
|
||||||
display_name: Some(row.nickname.unwrap_or(row.username)),
|
id: row.id,
|
||||||
user_id: row.user_id,
|
display_name: Some(row.nickname.unwrap_or(row.username)),
|
||||||
text: row.content,
|
user_id: row.user_id,
|
||||||
timestamp: row.created_at,
|
text: row.content,
|
||||||
})
|
timestamp: row.created_at,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl MessageRepository {
|
|
||||||
|
|
||||||
// TODO! caching with redis
|
// TODO! caching with redis
|
||||||
pub async fn create_new(
|
pub async fn create_new(
|
||||||
&self, uid: i64, channel_id: i64,
|
&self, msg: ChatMsg, channel_id: i64
|
||||||
text: &str, created_at: DateTime<Utc>
|
) -> Result<(), sqlx::Error> {
|
||||||
) -> Result<i64, sqlx::Error> {
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO messages (channel_id, user_id, content, created_at)
|
"INSERT INTO messages (id, channel_id, user_id, content, created_at)
|
||||||
VALUES ($1, $2, $3, $4) RETURNING id",
|
VALUES ($1, $2, $3, $4, $5)",
|
||||||
|
msg.id,
|
||||||
channel_id,
|
channel_id,
|
||||||
uid,
|
msg.user_id,
|
||||||
|
msg.text,
|
||||||
|
msg.timestamp
|
||||||
|
).execute(&self.pool).await.map_err(|_| sqlx::Error::RowNotFound)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_text(&self, id: Uuid, text: &str) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE messages SET content = $1 WHERE id = $2",
|
||||||
text,
|
text,
|
||||||
created_at
|
id
|
||||||
).fetch_optional(&self.pool).await.and_then(|row| row.map(|r| r.id).ok_or(sqlx::Error::RowNotFound))
|
).execute(&self.pool).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: caching with redis
|
/// TODO: caching with redis
|
||||||
pub async fn get_by_channel(&self, channel_id: i64, limit: usize)
|
pub async fn get_latest_by_channel_desc(
|
||||||
-> Result<Vec<ChatMsg>, sqlx::Error> {
|
&self, channel_id: i64, limit: usize, page: usize
|
||||||
|
) -> Result<Vec<ChatMsg>, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT u.username, u.nickname, u.id as user_id, m.content, m.created_at
|
"SELECT m.id, u.username, u.nickname, u.id as user_id, m.content, m.created_at
|
||||||
FROM messages m
|
FROM messages m
|
||||||
JOIN users u ON m.user_id = u.id
|
JOIN users u ON m.user_id = u.id
|
||||||
WHERE m.channel_id = $1
|
WHERE m.channel_id = $1
|
||||||
ORDER BY m.created_at DESC LIMIT $2",
|
ORDER BY m.created_at DESC
|
||||||
|
LIMIT $2 OFFSET $3",
|
||||||
channel_id,
|
channel_id,
|
||||||
limit as i64
|
limit as i64,
|
||||||
|
page as i64
|
||||||
).fetch_all(&self.pool).await.map(|messages| {
|
).fetch_all(&self.pool).await.map(|messages| {
|
||||||
messages.into_iter().rev().map(|msg| {
|
messages.into_iter().map(|msg| {
|
||||||
ChatMsg {
|
ChatMsg {
|
||||||
|
id: msg.id,
|
||||||
display_name: Some(msg.nickname.unwrap_or(msg.username)),
|
display_name: Some(msg.nickname.unwrap_or(msg.username)),
|
||||||
user_id: msg.user_id,
|
user_id: msg.user_id,
|
||||||
text: msg.content,
|
text: msg.content,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::repo::{UserRepo, AccessTokenRepoTrait};
|
use crate::repo::{UserRepo, AccessTokenRepoTrait};
|
||||||
use crate::model::user::User;
|
use crate::model::user::{User, UserRole};
|
||||||
use crate::model::auth::AccessToken;
|
use crate::model::auth::AccessToken;
|
||||||
use rocket::async_trait;
|
use rocket::async_trait;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@@ -59,6 +59,20 @@ impl UserRepo for MockUserRepo {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn is_admin(&self, uid: i64) -> Result<UserRole, Error> {
|
||||||
|
let user = self.users.lock().unwrap().iter().find(|u| u.id == uid).cloned();
|
||||||
|
if let Some(user) = user {
|
||||||
|
if user.id == 1 {
|
||||||
|
return Ok(UserRole::Admin);
|
||||||
|
} else {
|
||||||
|
return Ok(UserRole::User);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("user not found in test")
|
||||||
|
}
|
||||||
|
|
||||||
async fn new_user(&self, email: &str, username: &str, pass_hash: &str) -> Result<i64, sqlx::Error> {
|
async fn new_user(&self, email: &str, username: &str, pass_hash: &str) -> Result<i64, sqlx::Error> {
|
||||||
let mut users = self.users.lock().unwrap();
|
let mut users = self.users.lock().unwrap();
|
||||||
let id = users.len() as i64 + 1;
|
let id = users.len() as i64 + 1;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::model::auth::AccessToken;
|
use crate::model::auth::AccessToken;
|
||||||
use crate::model::user::User;
|
use crate::model::user::{User, UserRole};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use crate::api::totp::TotpStatus;
|
use crate::api::totp::TotpStatus;
|
||||||
use crate::model::space::Space;
|
use crate::model::space::Space;
|
||||||
@@ -25,6 +25,8 @@ pub trait UserRepo: Send + Sync {
|
|||||||
async fn get_by_id(&self, id: i64) -> Option<User>;
|
async fn get_by_id(&self, id: i64) -> Option<User>;
|
||||||
async fn save(&self, user: &User) -> Result<(), sqlx::Error>;
|
async fn save(&self, user: &User) -> Result<(), sqlx::Error>;
|
||||||
async fn new_user(&self, email: &str, username: &str, pass_hash: &str) -> Result<i64, sqlx::Error>;
|
async fn new_user(&self, email: &str, username: &str, pass_hash: &str) -> Result<i64, sqlx::Error>;
|
||||||
|
|
||||||
|
async fn is_admin(&self, uid: i64) -> Result<UserRole, sqlx::Error>;
|
||||||
async fn get_by_username(&self, username: &str) -> Result<Option<User>, sqlx::Error>;
|
async fn get_by_username(&self, username: &str) -> Result<Option<User>, sqlx::Error>;
|
||||||
async fn delete_by_id(&self, id: i64) -> Result<(), sqlx::Error>;
|
async fn delete_by_id(&self, id: i64) -> Result<(), sqlx::Error>;
|
||||||
async fn set_display_name(&self, id: i64, display_name: Option<String>) -> Result<(), sqlx::Error>;
|
async fn set_display_name(&self, id: i64, display_name: Option<String>) -> Result<(), sqlx::Error>;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use crate::repo::{Repo, UserRepo};
|
use crate::repo::{Repo, UserRepo};
|
||||||
use crate::model::user::User;
|
use crate::model::user::{User, UserRole};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use crate::api::totp::TotpStatus;
|
use crate::api::totp::TotpStatus;
|
||||||
|
use crate::model::user::UserRole::Admin;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UserRepository {
|
pub struct UserRepository {
|
||||||
@@ -66,6 +67,15 @@ impl UserRepo for UserRepository {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn is_admin(&self, uid: i64) -> Result<UserRole, sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"SELECT role AS \"user_role!: UserRole\" FROM users WHERE id = $1", uid
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map(|row| row.user_role)
|
||||||
|
}
|
||||||
|
|
||||||
async fn new_user(&self, email: &str, username: &str, passhash: &str) -> Result<i64, sqlx::Error> {
|
async fn new_user(&self, email: &str, username: &str, passhash: &str) -> Result<i64, sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO users (email, username, passhash) VALUES ($1, $2, $3) RETURNING id",
|
"INSERT INTO users (email, username, passhash) VALUES ($1, $2, $3) RETURNING id",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::api::totp::TotpStatus::{Disabled, Enabled};
|
use crate::api::totp::TotpStatus::{Disabled, Enabled};
|
||||||
|
use crate::model::user::UserRole::Admin;
|
||||||
use crate::svc::access_token_svc::AccessTokenService;
|
use crate::svc::access_token_svc::AccessTokenService;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -103,6 +104,10 @@ impl AuthService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn is_admin(&self, uid: i64) -> ApiResult<bool> {
|
||||||
|
Ok(self.users.is_admin(uid).await? == Admin)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_totp_status(&self, uid: i64) -> ApiResult<bool> {
|
pub async fn get_totp_status(&self, uid: i64) -> ApiResult<bool> {
|
||||||
Ok(
|
Ok(
|
||||||
self.users.get_totp_secret(uid).await?.is_some()
|
self.users.get_totp_secret(uid).await?.is_some()
|
||||||
|
|||||||
+107
-45
@@ -1,12 +1,15 @@
|
|||||||
use crate::api::chat::ChatMsg;
|
use crate::model::event::{ChatEvent, ChatMsg};
|
||||||
use crate::error::{ApiResult, AppError};
|
use crate::error::{ApiResult, AppError};
|
||||||
use crate::repo::message_repo::MessageRepository;
|
use crate::repo::message_repo::MessageRepository;
|
||||||
use crate::repo::{ChannelRepo, Repo, SpaceRepo, UserRepo};
|
use crate::repo::{ChannelRepo, Repo, SpaceRepo, UserRepo};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
use std::time::Instant;
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
use tokio::sync::{broadcast, Mutex};
|
use tokio::sync::{broadcast, Mutex};
|
||||||
|
use uuid::Uuid;
|
||||||
use crate::model::space::SpaceDto;
|
use crate::model::space::SpaceDto;
|
||||||
use crate::svc::llm_service::LlmService;
|
use crate::svc::llm_service::LlmService;
|
||||||
|
|
||||||
@@ -15,15 +18,13 @@ use crate::svc::llm_service::LlmService;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ChatService {
|
pub struct ChatService {
|
||||||
users: Arc<dyn UserRepo>,
|
users: Arc<dyn UserRepo>,
|
||||||
channels: Arc<dyn ChannelRepo>,
|
channel_repo: Arc<dyn ChannelRepo>,
|
||||||
spaces: Arc<dyn SpaceRepo>,
|
spaces: Arc<dyn SpaceRepo>,
|
||||||
messages: MessageRepository,
|
messages: MessageRepository,
|
||||||
|
|
||||||
llm: LlmService,
|
llm: LlmService,
|
||||||
buffer_size: usize,
|
buffer_size: usize,
|
||||||
senders: Arc<Mutex<HashMap<i64, Sender<ChatMsg>>>>,
|
channels: Arc<Mutex<HashMap<i64, ChannelState>>>,
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatService {
|
impl ChatService {
|
||||||
@@ -33,13 +34,13 @@ impl ChatService {
|
|||||||
channels: Arc<dyn ChannelRepo>, spaces: Arc<dyn SpaceRepo>,
|
channels: Arc<dyn ChannelRepo>, spaces: Arc<dyn SpaceRepo>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
channels,
|
channel_repo: channels,
|
||||||
spaces,
|
spaces,
|
||||||
llm,
|
llm,
|
||||||
users,
|
users,
|
||||||
messages,
|
messages,
|
||||||
buffer_size,
|
buffer_size,
|
||||||
senders: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
channels: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ impl ChatService {
|
|||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for space in spaces {
|
for space in spaces {
|
||||||
let channels = self.channels.get_by_space_id(space.id).await?;
|
let channels = self.channel_repo.get_by_space_id(space.id).await?;
|
||||||
result.push(SpaceDto {
|
result.push(SpaceDto {
|
||||||
channels,
|
channels,
|
||||||
id: space.id,
|
id: space.id,
|
||||||
@@ -65,8 +66,10 @@ impl ChatService {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_messages(&self, channel_id: i64, limit: usize) -> ApiResult<Vec<ChatMsg>> {
|
pub async fn fetch_latest_messages_desc(&self, channel_id: i64, limit: usize) -> ApiResult<Vec<ChatMsg>> {
|
||||||
let messages = self.messages.get_by_channel(channel_id, limit).await?;
|
const PAGE: usize = 0;
|
||||||
|
|
||||||
|
let messages = self.messages.get_latest_by_channel_desc(channel_id, limit, PAGE).await?;
|
||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +116,7 @@ impl ChatService {
|
|||||||
.ok_or(AppError::NotFound)?;
|
.ok_or(AppError::NotFound)?;
|
||||||
|
|
||||||
let message = ChatMsg {
|
let message = ChatMsg {
|
||||||
|
id: Uuid::new_v4(),
|
||||||
display_name: Some(user
|
display_name: Some(user
|
||||||
.nickname.clone()
|
.nickname.clone()
|
||||||
.unwrap_or_else(|| user.username.clone())),
|
.unwrap_or_else(|| user.username.clone())),
|
||||||
@@ -122,66 +126,124 @@ impl ChatService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.publish(channel_id, message.clone()).await;
|
self.publish(channel_id, message.clone()).await;
|
||||||
|
self.messages.create_new(message.clone(), channel_id).await?;
|
||||||
let _msg_id = self.messages.create_new(uid, channel_id, text, created_at).await?;
|
|
||||||
// TODO: caching w redis at repository layer
|
// TODO: caching w redis at repository layer
|
||||||
|
|
||||||
let svc_instance = self.clone();
|
if !message.text.starts_with("/ask ") {
|
||||||
|
return Ok(());
|
||||||
let Some(text) = text.strip_prefix("/ask ") else {
|
|
||||||
return Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
if !svc_instance.llm.enabled() {
|
|
||||||
return Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let svc_instance = self.clone();
|
||||||
|
if !svc_instance.llm.enabled() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let context = self.get_history(channel_id, 25).await?;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
let sender = match svc_instance.channels.lock().await.get(&channel_id) {
|
||||||
|
Some(s) => s.get_sender(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
let response = svc_instance.llm
|
let response = svc_instance.llm
|
||||||
.query(&message)
|
.query(&message, &context, sender)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Ok(reply) = response {
|
let Ok(reply) = response else {
|
||||||
|
|
||||||
tracing::info!("LLM reply: {}", reply.text);
|
|
||||||
|
|
||||||
svc_instance.publish(channel_id, reply.clone()).await;
|
|
||||||
// TODO: cache response (or do with redis!)
|
|
||||||
if let Err(e) = svc_instance.messages
|
|
||||||
.create_new(reply.user_id, channel_id, &reply.text, reply.timestamp).await {
|
|
||||||
tracing::error!("Failed to persist LLM reply: {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::info!("LLM reply persisted");
|
|
||||||
|
|
||||||
} else {
|
|
||||||
tracing::warn!("Error contacting LLM: {:?}", response);
|
tracing::warn!("Error contacting LLM: {:?}", response);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: cache response (or do with redis!)
|
||||||
|
if let Err(e) = svc_instance.messages.create_new(reply, channel_id).await {
|
||||||
|
tracing::error!("Failed to persist LLM reply: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::debug!("Full LLM reply persisted");
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_history(&self, channel_id: i64, limit: usize) -> ApiResult<Vec<ChatMsg>> {
|
||||||
|
let mut map = self.channels.lock().await;
|
||||||
|
if let Some(channel) = map.get(&channel_id) && !channel.cache.is_empty() {
|
||||||
|
Ok(channel.history().clone().into_iter().take(limit).collect())
|
||||||
|
} else {
|
||||||
|
let messages: Vec<_> = self.messages.get_latest_by_channel_desc(channel_id, limit, 0).await?.into_iter().rev().collect();
|
||||||
|
map.insert(channel_id,
|
||||||
|
ChannelState::new(self.buffer_size, Some(messages.clone().into()))
|
||||||
|
);
|
||||||
|
Ok(messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subscribe to the specified channel.
|
/// Subscribe to the specified channel.
|
||||||
pub async fn subscribe(&self, channel_id: i64) -> broadcast::Receiver<ChatMsg> {
|
pub async fn subscribe(&self, channel_id: i64) -> broadcast::Receiver<ChatEvent> {
|
||||||
let mut map = self.senders.lock().await;
|
let mut map = self.channels.lock().await;
|
||||||
let sender = map
|
let channel = map
|
||||||
.entry(channel_id)
|
.entry(channel_id)
|
||||||
.or_insert_with(|| broadcast::channel::<ChatMsg>(self.buffer_size).0);
|
.or_insert_with(|| ChannelState::new(self.buffer_size, None));
|
||||||
sender.subscribe()
|
channel.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private helper methods
|
// Private helper methods
|
||||||
|
|
||||||
/// Publish a message to the specified channel.
|
/// Publish a message to the specified channel.
|
||||||
async fn publish(&self, channel_id: i64, msg: ChatMsg) {
|
async fn publish(&self, channel_id: i64, msg: ChatMsg) {
|
||||||
let mut map = self.senders.lock().await;
|
let mut map = self.channels.lock().await;
|
||||||
let sender = map
|
let channel = map
|
||||||
.entry(channel_id)
|
.entry(channel_id)
|
||||||
.or_insert_with(|| broadcast::channel::<ChatMsg>(self.buffer_size).0);
|
.or_insert_with(|| ChannelState::new(self.buffer_size, None));
|
||||||
let _ = sender.send(msg);
|
channel.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ChannelState {
|
||||||
|
sender: Sender<ChatEvent>,
|
||||||
|
cache: VecDeque<ChatMsg>,
|
||||||
|
last_updated: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChannelState {
|
||||||
|
|
||||||
|
const MAX_HISTORY_SIZE: usize = 100;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(buffer_size: usize, history: Option<VecDeque<ChatMsg>>) -> Self {
|
||||||
|
Self {
|
||||||
|
sender: broadcast::channel(buffer_size).0,
|
||||||
|
cache: history.unwrap_or_default(),
|
||||||
|
last_updated: Instant::now(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn history(&self) -> &VecDeque<ChatMsg> {
|
||||||
|
&self.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sender(&self) -> Sender<ChatEvent> {
|
||||||
|
self.sender.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn send(&mut self, msg: ChatMsg) {
|
||||||
|
while self.cache.len() >= Self::MAX_HISTORY_SIZE {
|
||||||
|
self.cache.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.push_back(msg.clone());
|
||||||
|
|
||||||
|
if self.sender.send(ChatEvent::SendMessage(msg)).is_err() {
|
||||||
|
tracing::warn!("Sent message to channel with no subscribers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn subscribe(&self) -> broadcast::Receiver<ChatEvent> {
|
||||||
|
self.sender.subscribe()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+119
-41
@@ -1,3 +1,13 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use futures_util::StreamExt;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::broadcast::Sender;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use crate::model::event::{ChatEvent, ChatMsg};
|
||||||
|
use crate::error::{ApiResult, AppError};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LlmService;
|
pub struct LlmService;
|
||||||
|
|
||||||
@@ -15,71 +25,139 @@ impl LlmService {
|
|||||||
LMSTUDIO_URL.is_some()
|
LMSTUDIO_URL.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn query(&self, message: &ChatMsg) -> ApiResult<ChatMsg> {
|
pub async fn query(&self, message: &ChatMsg, context: &[ChatMsg], sender: Sender<ChatEvent>) -> ApiResult<ChatMsg> {
|
||||||
let Some(url) = LMSTUDIO_URL.clone() else {
|
let Some(url) = LMSTUDIO_URL.clone() else {
|
||||||
return Err(AppError::internal("AI not enabled!"))
|
return Err(AppError::internal("AI not enabled!"))
|
||||||
};
|
};
|
||||||
|
|
||||||
let model = LMSTUDIO_MODEL.clone().unwrap_or_else(|| "gpt-oss-20b".into());
|
let reply_id = Uuid::new_v4();
|
||||||
|
let timestamp = chrono::Utc::now();
|
||||||
let client = reqwest::Client::new();
|
let mut reply = ChatMsg {
|
||||||
|
id: reply_id,
|
||||||
// Build the request body
|
display_name: Some("llm".into()),
|
||||||
let payload = LlmRequest {
|
user_id: 0,
|
||||||
model, // whatever model you run locally
|
text: String::new(),
|
||||||
messages: vec![Message {
|
timestamp,
|
||||||
role: "user".into(),
|
|
||||||
content: message.text.clone(),
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
|
let _ = sender.send(ChatEvent::SendMessage(reply.clone()));
|
||||||
|
|
||||||
// POST to lm‑studio (default 127.0.0.1:1234)
|
let mut messages: Vec<Message> = Vec::new();
|
||||||
let resp = client
|
let system_prompt = format!(
|
||||||
|
"You are a helpful assistant in a group chat. \
|
||||||
|
You are talking to '{}'. \
|
||||||
|
Keep responses concise and conversational.",
|
||||||
|
message.display_name.as_deref().unwrap_or("unknown"),
|
||||||
|
);
|
||||||
|
messages.push(Message { role: "system".into(), content: system_prompt });
|
||||||
|
|
||||||
|
for msg in context {
|
||||||
|
let role = if msg.user_id == 0 {
|
||||||
|
"assistant" // your LLM user_id convention
|
||||||
|
} else {
|
||||||
|
"user"
|
||||||
|
};
|
||||||
|
messages.push(Message {
|
||||||
|
role: role.into(),
|
||||||
|
content: format!(
|
||||||
|
"{}: {}",
|
||||||
|
msg.display_name.as_deref().unwrap_or("unknown"),
|
||||||
|
msg.text
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.push(Message {
|
||||||
|
role: "user".into(),
|
||||||
|
content: format!(
|
||||||
|
"{}: {}",
|
||||||
|
message.display_name.as_deref().unwrap_or("unknown"),
|
||||||
|
message.text.trim_start_matches("/ask ") // strip the command prefix
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
let Ok(resp) = reqwest::Client::new()
|
||||||
.post(url)
|
.post(url)
|
||||||
.json(&payload)
|
.json(&LlmRequest {
|
||||||
|
think: false,
|
||||||
|
model: LMSTUDIO_MODEL
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "gpt-oss-20b".into()),
|
||||||
|
messages,
|
||||||
|
stream: true,
|
||||||
|
})
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(|_| AppError::internal("Failed to make request to LLM server"))?;
|
else {
|
||||||
|
tracing::warn!("Failed to reach LLM");
|
||||||
|
let _ = sender.send(ChatEvent::MessageAppendContent {
|
||||||
|
id: reply_id,
|
||||||
|
content: String::from("I'm not available right now. Please try again later.")
|
||||||
|
});
|
||||||
|
return Err(AppError::internal("Failed to reach LLM"));
|
||||||
|
};
|
||||||
|
|
||||||
// The API returns a JSON with `choices[].message.content`
|
|
||||||
#[derive(Deserialize)]
|
let mut full_text = String::new();
|
||||||
struct LlmResponse {
|
let mut buffer = String::new();
|
||||||
choices: Vec<Choice>,
|
let mut stream = resp.bytes_stream();
|
||||||
}
|
|
||||||
#[derive(Deserialize)]
|
while let Some(chunk) = stream.next().await {
|
||||||
struct Choice {
|
let chunk = chunk.map_err(|_| AppError::internal("Stream error"))?;
|
||||||
message: Message,
|
buffer.push_str(&String::from_utf8_lossy(&chunk));
|
||||||
|
|
||||||
|
while let Some(pos) = buffer.find('\n') {
|
||||||
|
let line = buffer[..pos].trim().to_string();
|
||||||
|
buffer = buffer[pos + 1..].to_string();
|
||||||
|
|
||||||
|
if line == "data: [DONE]" {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(json) = line.strip_prefix("data: ")
|
||||||
|
&& let Ok(parsed) = serde_json::from_str::<StreamingResponse>(json)
|
||||||
|
&& let Some(content) = parsed.choices[0].delta.content.as_ref() {
|
||||||
|
full_text.push_str(content);
|
||||||
|
let _ = sender.send(ChatEvent::MessageAppendContent {
|
||||||
|
id: reply_id,
|
||||||
|
content: content.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let llm_resp: LlmResponse = resp
|
reply.text = full_text;
|
||||||
.json()
|
|
||||||
.await
|
|
||||||
.map_err(|_| AppError::internal("Failed to parse LLM response"))?;
|
|
||||||
|
|
||||||
Ok(ChatMsg {
|
Ok(reply)
|
||||||
display_name: Some(String::from("llm")),
|
|
||||||
user_id: 0,
|
|
||||||
text: llm_resp.choices[0].message.content.clone(),
|
|
||||||
timestamp: chrono::Utc::now(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
// src/llm.rs
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::api::chat::ChatMsg;
|
|
||||||
use crate::error::{ApiResult, AppError};
|
|
||||||
use crate::svc::chat_svc::ChatService;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct LlmRequest {
|
struct LlmRequest {
|
||||||
model: String,
|
model: String,
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
|
stream: bool,
|
||||||
|
think: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct StreamingResponse {
|
||||||
|
choices: Vec<StreamingChoice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct StreamingChoice {
|
||||||
|
delta: Delta,
|
||||||
|
#[serde(default)]
|
||||||
|
finish_reason: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Delta {
|
||||||
|
#[serde(default)]
|
||||||
|
content: Option<String>,
|
||||||
|
}
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct Message {
|
struct Message {
|
||||||
role: String, // "user" or "assistant"
|
role: String, // "user" or "assistant"
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ pub mod settings_svc;
|
|||||||
pub mod user_svc;
|
pub mod user_svc;
|
||||||
pub mod access_token_svc;
|
pub mod access_token_svc;
|
||||||
pub mod llm_service;
|
pub mod llm_service;
|
||||||
|
pub mod relationship_svc;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub struct RelationshipService {}
|
||||||
|
|
||||||
|
impl RelationshipService {
|
||||||
|
pub fn new() -> Self { Self {} }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,935 +0,0 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: #0a0a0a;
|
|
||||||
color: #e0e0e0;
|
|
||||||
max-height: 100dvh;
|
|
||||||
min-width: 100dvw;
|
|
||||||
height: 100dvh;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background: #0a0a0a;
|
|
||||||
color: #e0e0e0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-container {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Chat Container */
|
|
||||||
.chat-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
height: 100dvh;
|
|
||||||
background: #121212;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Chat Header */
|
|
||||||
.chat-header {
|
|
||||||
padding: 10px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
border-bottom: 1px solid #252525;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: #ff4444;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: pulse 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
transform: scale(1);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.3);
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-title h1 {
|
|
||||||
font-size: 17px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Live Location Notification Bubble */
|
|
||||||
.notification-container {
|
|
||||||
padding: 15px 20px 10px 20px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
position: absolute;
|
|
||||||
top: 70px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 10;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble {
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 20px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
border: 1px solid rgba(138, 43, 226, 0.3);
|
|
||||||
box-shadow:
|
|
||||||
0 8px 32px rgba(106, 90, 205, 0.2),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
||||||
pointer-events: auto;
|
|
||||||
max-height: 64px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(106, 90, 205, 0.25) 0%,
|
|
||||||
rgba(72, 61, 139, 0.2) 50%,
|
|
||||||
rgba(123, 104, 238, 0.15) 100%
|
|
||||||
);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded::before {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble > * {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded {
|
|
||||||
max-height: 400px;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded .map-container {
|
|
||||||
height: 260px;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-container img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 16px 20px;
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(106, 90, 205, 0.25) 0%,
|
|
||||||
rgba(72, 61, 139, 0.2) 50%,
|
|
||||||
rgba(123, 104, 238, 0.15) 100%
|
|
||||||
);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded .location-content {
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom,
|
|
||||||
rgba(106, 90, 205, 0.45) 0%,
|
|
||||||
rgba(72, 61, 139, 0.4) 50%,
|
|
||||||
rgba(123, 104, 238, 0.35) 100%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
transparent,
|
|
||||||
rgba(255, 255, 255, 0.2),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow:
|
|
||||||
0 12px 40px rgba(106, 90, 205, 0.35),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(106, 90, 205, 0.3) 0%,
|
|
||||||
rgba(72, 61, 139, 0.25) 50%,
|
|
||||||
rgba(123, 104, 238, 0.2) 100%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.active {
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(106, 90, 205, 0.35) 0%,
|
|
||||||
rgba(72, 61, 139, 0.3) 50%,
|
|
||||||
rgba(123, 104, 238, 0.25) 100%
|
|
||||||
);
|
|
||||||
border-color: rgba(138, 43, 226, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-icon {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-icon svg {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
fill: #ea4335;
|
|
||||||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded .location-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-button {
|
|
||||||
display: none;
|
|
||||||
padding: 8px 20px;
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
border-radius: 20px;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.expanded .join-button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-button:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.25);
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-button:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-button.active {
|
|
||||||
background: rgba(106, 90, 205, 0.4);
|
|
||||||
border-color: rgba(138, 43, 226, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-location-bubble.active .location-icon {
|
|
||||||
background: rgba(255, 255, 255, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-text {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #ffffff;
|
|
||||||
flex: 1;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-users {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-user-pic {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 35%;
|
|
||||||
margin-left: -15px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-user-pic:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-user-pic:hover {
|
|
||||||
z-index: 10;
|
|
||||||
transform: scale(1.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.location-count {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 35%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
margin-left: -15px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #ffffff;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages-container::before {
|
|
||||||
content: "";
|
|
||||||
flex: 1 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Messages Container */
|
|
||||||
.messages-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
/*justify-content: flex-end;*/
|
|
||||||
flex: 1;
|
|
||||||
padding: 15px;
|
|
||||||
gap: 15px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
background: #121212;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: rgba(30, 30, 30, 0.4);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border: 1px solid #252525;
|
|
||||||
animation: slideIn 0.3s ease-out;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message:hover {
|
|
||||||
background: #1f1f1f;
|
|
||||||
border-color: #2a2a2a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border-radius: 25%;
|
|
||||||
background-size: cover;
|
|
||||||
border: 2px solid #252525;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-avatar.blue {
|
|
||||||
background: linear-gradient(135deg, #4169e1, #6495ed);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-content {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.username {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 15px;
|
|
||||||
color: #e0e0e0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestamp {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #666;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #b0b0b0;
|
|
||||||
line-height: 1.4;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Input Container */
|
|
||||||
.input-container {
|
|
||||||
padding: 10px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
border-top: 1px solid #252525;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
background: #252525;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 5px 5px;
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrapper:focus-within {
|
|
||||||
background: #2a2a2a;
|
|
||||||
border-color: #6a5acd;
|
|
||||||
box-shadow: 0 0 0 3px rgba(106, 90, 205, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrapper input {
|
|
||||||
flex: 1;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: #e0e0e0;
|
|
||||||
font-size: 14px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrapper input::placeholder {
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-button {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
background: none;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-button:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
box-shadow: 0 4px 15px rgba(106, 90, 205, 0.4);
|
|
||||||
background: rgba(255, 255, 255, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-button:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar styling */
|
|
||||||
.messages-container::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages-container::-webkit-scrollbar-track {
|
|
||||||
background: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages-container::-webkit-scrollbar-thumb {
|
|
||||||
background: #333;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messages-container::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
background: #121212;
|
|
||||||
border-radius: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
||||||
animation: slideIn 0.4s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-header {
|
|
||||||
padding: 40px 40px 30px;
|
|
||||||
text-align: center;
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(106, 90, 205, 0.15) 0%,
|
|
||||||
rgba(72, 61, 139, 0.1) 100%
|
|
||||||
);
|
|
||||||
border-bottom: 1px solid #252525;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
margin: 0 auto 20px;
|
|
||||||
background: linear-gradient(135deg, #6a5acd, #483d8b);
|
|
||||||
border-radius: 25%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 36px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #ffffff;
|
|
||||||
box-shadow: 0 4px 16px rgba(106, 90, 205, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-header h1 {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #ffffff;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-header p {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-form {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #b0b0b0;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
border: 1px solid #252525;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: #e0e0e0;
|
|
||||||
font-size: 15px;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input:focus {
|
|
||||||
background: #1f1f1f;
|
|
||||||
border-color: #6a5acd;
|
|
||||||
box-shadow: 0 0 0 3px rgba(106, 90, 205, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input::placeholder {
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message {
|
|
||||||
display: none;
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #ff4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group.error input {
|
|
||||||
border-color: #ff4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group.error .error-message {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-toggle {
|
|
||||||
position: absolute;
|
|
||||||
right: 12px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: #666;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-toggle:hover {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 14px;
|
|
||||||
background: linear-gradient(135deg, #6a5acd, #483d8b);
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 4px 16px rgba(106, 90, 205, 0.3);
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button:hover {
|
|
||||||
background: linear-gradient(135deg, #7b6bd8, #5a4ea0);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 20px rgba(106, 90, 205, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button:disabled {
|
|
||||||
background: #2a2a2a;
|
|
||||||
color: #555;
|
|
||||||
cursor: not-allowed;
|
|
||||||
transform: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-footer {
|
|
||||||
padding: 20px 40px 30px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-footer a {
|
|
||||||
color: #6a5acd;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signup-footer a:hover {
|
|
||||||
color: #7b6bd8;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success-message {
|
|
||||||
display: none;
|
|
||||||
padding: 16px;
|
|
||||||
background: rgba(76, 175, 80, 0.1);
|
|
||||||
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
||||||
border-radius: 8px;
|
|
||||||
color: #4caf50;
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success-message.show {
|
|
||||||
display: block;
|
|
||||||
animation: slideIn 0.3s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
display: inline-block;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
border-top-color: #ffffff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 0.6s linear infinite;
|
|
||||||
margin-right: 8px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group input[type="checkbox"] {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
margin-top: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
accent-color: #6a5acd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group label {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #888;
|
|
||||||
line-height: 1.5;
|
|
||||||
text-transform: none;
|
|
||||||
letter-spacing: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group a {
|
|
||||||
color: #6a5acd;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar Styles */
|
|
||||||
.sidebar {
|
|
||||||
width: 240px;
|
|
||||||
min-width: 180px;
|
|
||||||
max-width: 400px;
|
|
||||||
background: #0f0f0f;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-right: 1px solid #252525;
|
|
||||||
position: relative;
|
|
||||||
transition:
|
|
||||||
margin-left 0.3s ease,
|
|
||||||
opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.hidden {
|
|
||||||
margin-left: -240px;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-handle {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 4px;
|
|
||||||
cursor: ew-resize;
|
|
||||||
background: transparent;
|
|
||||||
transition: background 0.2s ease;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-handle:hover {
|
|
||||||
background: rgba(106, 90, 205, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-handle:active {
|
|
||||||
background: #6a5acd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-header {
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: 1px solid #252525;
|
|
||||||
background: #1a1a1a;
|
|
||||||
color: #ffffff;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 15px;
|
|
||||||
letter-spacing: 0.3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channels-list {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 10px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-item {
|
|
||||||
padding: 10px 12px;
|
|
||||||
margin: 3px 0;
|
|
||||||
border-radius: 8px;
|
|
||||||
color: #b0b0b0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
background: rgba(30, 30, 30, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-item:hover {
|
|
||||||
background: rgba(30, 30, 30, 0.6);
|
|
||||||
border-color: #2a2a2a;
|
|
||||||
color: #e0e0e0;
|
|
||||||
transform: translateX(2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-item.active {
|
|
||||||
background: rgba(106, 90, 205, 0.15);
|
|
||||||
border-color: rgba(106, 90, 205, 0.3);
|
|
||||||
color: #ffffff;
|
|
||||||
box-shadow: 0 2px 8px rgba(106, 90, 205, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-icon {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #666;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-item:hover .channel-icon {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-item.active .channel-icon {
|
|
||||||
color: #6a5acd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scrollbar styling for sidebar */
|
|
||||||
.channels-list::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channels-list::-webkit-scrollbar-track {
|
|
||||||
background: #0f0f0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channels-list::-webkit-scrollbar-thumb {
|
|
||||||
background: #252525;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channels-list::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-toggle {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
background: rgba(30, 30, 30, 0.6);
|
|
||||||
border: 1px solid #2a2a2a;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: #b0b0b0;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-toggle:hover {
|
|
||||||
background: rgba(106, 90, 205, 0.2);
|
|
||||||
border-color: rgba(106, 90, 205, 0.3);
|
|
||||||
color: #e0e0e0;
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-toggle:active {
|
|
||||||
transform: scale(0.95);
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
@@ -1,169 +0,0 @@
|
|||||||
## Subresource Integrity
|
|
||||||
|
|
||||||
If you are loading Highlight.js via CDN you may wish to use [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) to guarantee that you are using a legimitate build of the library.
|
|
||||||
|
|
||||||
To do this you simply need to add the `integrity` attribute for each JavaScript file you download via CDN. These digests are used by the browser to confirm the files downloaded have not been modified.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script
|
|
||||||
src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"
|
|
||||||
integrity="sha384-5xdYoZ0Lt6Jw8GFfRP91J0jaOVUq7DGI1J5wIyNi0D+eHVdfUwHR4gW6kPsw489E"></script>
|
|
||||||
<!-- including any other grammars you might need to load -->
|
|
||||||
<script
|
|
||||||
src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js"
|
|
||||||
integrity="sha384-HdearVH8cyfzwBIQOjL/6dSEmZxQ5rJRezN7spps8E7iu+R6utS8c2ab0AgBNFfH"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
The full list of digests for every file can be found below.
|
|
||||||
|
|
||||||
### Digests
|
|
||||||
|
|
||||||
```
|
|
||||||
sha384-gRTR/fmk+6+ygbihH/fJvHgmffnOrd/eO7DW5zgu1uN9GBohtDx+OBs0DI0ejigB /es/languages/bash.js
|
|
||||||
sha384-Pg7b9hYE6kefjcNqAabhv8jOLCVoZubUaM4bZFjUJd0CaaQ14ksDI0GVllMtAF4S /es/languages/bash.min.js
|
|
||||||
sha384-xhohaHGp8S443Qn4JZUYAcKqIIl0bQkFA79EUxpbX8GWb5oufdvvSI9ipl/Dasev /es/languages/c.js
|
|
||||||
sha384-xaTVEdq02jgKStoYDcZD8NhTN1XV/TWpIu4OM53MtMiLl08+e9YJNENo+R/6Nwp0 /es/languages/c.min.js
|
|
||||||
sha384-rFCBWxbZHxZD51qKR2cdayIcKUSHS3p1PWPIs1kjgsP7lu9ZP32ah/2DoQUm/rTg /es/languages/cpp.js
|
|
||||||
sha384-+1Koxl0St78gEZW5CpFK+dbLp7yNsfwLzzQUsSGimV4k/RVJUz6YvqtsqtdbJyKf /es/languages/cpp.min.js
|
|
||||||
sha384-0s8f7nphuRu8IIkFNCeOVZhvbjt7YKZEHl38OjfkCkdtnwIUvwRNbxxUHkCdcYjm /es/languages/csharp.js
|
|
||||||
sha384-xLfGW0hIBHie9xNFuVroNihI0BdEO8FKxOeCdyJBrO1eM7s5BsQ8F3fLtFydQZ+Z /es/languages/csharp.min.js
|
|
||||||
sha384-Gmvct15f4Mo9AXQG5bk5w78N1YjBLXXU3KIV7no+mOVnApXlwfw1dwjfueAwljIV /es/languages/css.js
|
|
||||||
sha384-1D7DbOic0Z5nM2ldSO9O/EsBfsg/5x7X7So1qnMgscI2ucDevptcg7cTvrD9rL0D /es/languages/css.min.js
|
|
||||||
sha384-CxgzMCYCdPS3oPSgukCqpDiqHKDKcrLdlyMqy9UF73u63+XVRlI32OproboitNa6 /es/languages/diff.js
|
|
||||||
sha384-joI34L4jMJOgkz6zOb3sqraHH5tmocRfXvs9HkdHfUpD3ceSxAqKlubpBT/4Q/sV /es/languages/diff.min.js
|
|
||||||
sha384-y5tpDG/EgM93k2unGm4XFn8l9V12Ru1tnk2TxhduZWqrEqAK86BQlDLuVAILe3OB /es/languages/go.js
|
|
||||||
sha384-/UGh0AcfdC41Di2LsNVYCPOJ24RfaUWWXniaZoGuM52DaQR7fwStKAHJumI+u5yY /es/languages/go.min.js
|
|
||||||
sha384-opjEyp4yq3g7Bxr5w5ZcG0+fTUsnOc798DWlLPzWPFIV+qGmKGkzzZIAr3822+oQ /es/languages/graphql.js
|
|
||||||
sha384-ycqZEp/d+8Lmf+CQrUvmfgcQkd5TJPU36i1WFGNiK18rlap/VJSTcPfSj/ft5Faf /es/languages/graphql.min.js
|
|
||||||
sha384-vZWLk+C+23/W/GAmv4PXkZSZo82LXul6DdSgWcMzutPxGltitIk38HyLrxRVsFvm /es/languages/ini.js
|
|
||||||
sha384-CVynu7LzwkkAUiajSi0jprssYhgg9Vi1WSd8iR84Vmi6JdRGq3DT4vvEfjzoxxOK /es/languages/ini.min.js
|
|
||||||
sha384-lk+aAr+DNq8Rz3hXPSZ7ga38GS+tQfXDvexuUnyDCSju1t1SAsLipVIFGlRtcUjE /es/languages/java.js
|
|
||||||
sha384-5GpB6kfA2w03pZhAUmmNSYvR5pLvne/Rzqc22BmHv+t9ES7ifMX/ZE7x5TBeqW4d /es/languages/java.min.js
|
|
||||||
sha384-g7t9fKR5Tvod4iWv7BQXN+/JMn5GT9sD6FG3h7Fgl+KCv5k4NnnCzEqUe7BMJ9Mv /es/languages/javascript.js
|
|
||||||
sha384-f7huPivS1dV2T5V+g0aJpgsY7WBHWCsioIq30tpNoXGizD65fWJYGuXXVPNI52VB /es/languages/javascript.min.js
|
|
||||||
sha384-8CRS96Xb/ZkZlQU+5ffA03XTN6/xY40QAnsXKB0Y+ow1vza1LAkRNPSrZqGSNo53 /es/languages/json.js
|
|
||||||
sha384-UHzaYxI/rAo84TEK3WlG15gVfPk49XKax76Ccn9qPWYbUxePCEHxjGkV+xp9HcS/ /es/languages/json.min.js
|
|
||||||
sha384-74O59Gvm0duu3aXH7S8RHhqn8YvAF1JlgCdNDq5MaClY/f/0bMs4zryv55Whwp2c /es/languages/kotlin.js
|
|
||||||
sha384-+aJFpyNBGTRiXRDN6BLrctauQBKExwSosxOiHLUYReXKTsckW/RgMavqX6W+zTBL /es/languages/kotlin.min.js
|
|
||||||
sha384-LRBDaxnf3ea3MTosn2yHFNe+ECfow/i4s71k6UdzkNOS1QvgHkcqRBTkDZC5aEoP /es/languages/less.js
|
|
||||||
sha384-EJ7n9HlCUKgtcBomJlrocJe2M2WegUc2r/TqymQdykuxcLeA25bQ5665qN58BWki /es/languages/less.min.js
|
|
||||||
sha384-5TnIBSbRIGDilxscXgaTNLZ8PZ9u7TEBPzF8b9z+wrbTN3e89MbD9zSSuDVdbDFj /es/languages/lua.js
|
|
||||||
sha384-HCBq0pjgKyOc3FNX31to33MxfNYza3HCbHLfWwdsnkH5r/VmmXTlRrvWSHTJyYvA /es/languages/lua.min.js
|
|
||||||
sha384-gXOyhc1mLm8alPswShRUsxnBadQf2AOcdpmFVqkKSZBNS07kTOHb3DDepD3Rf9eH /es/languages/makefile.js
|
|
||||||
sha384-iagI2W32nmVJq2EVd59zlgz2bFjR/MC0JRsbcFaeZL2gmvHzzOeyRTpTKGBSKd7q /es/languages/makefile.min.js
|
|
||||||
sha384-+KkqXkoHKtuOmUzhZ0BjyV0qjljnS+z6i4fELMEg5brFPtmDIog4zZMhylaBTsVi /es/languages/markdown.js
|
|
||||||
sha384-E7UvgBH6skA1FIOcn3B2c68GtJzrmZlOOC5p/fsxwihTZG/bBedJZu5PC1+kGX7q /es/languages/markdown.min.js
|
|
||||||
sha384-JoTXwNHosdzUqxg9EepqeL0yHV98o2Fy7EwTtN9awR+5d8T1EQRXmpdHjpRjxkUM /es/languages/objectivec.js
|
|
||||||
sha384-IA3s+KMOVf9zn8hH1+u4PCkGcunD2O3mNj4y10RDG/Wq7Gs7QWgiH6o01bjXeU1b /es/languages/objectivec.min.js
|
|
||||||
sha384-uat5CiOqKMoJM3KrhuhZShpWQriLSdeaJ4oT6XQFBg3YWhVDiofp4wa0SnBcV8LQ /es/languages/perl.js
|
|
||||||
sha384-doJqxKOhDf9Q9JYEBQXrTguSjzwPrbLiXJP2kdBkF+BYRNP55btwz9iH4b95mUTH /es/languages/perl.min.js
|
|
||||||
sha384-4OPZSHQbxzPqFMOXnndxQ6TZTI/B+J4W9aqTCHxAx/dsPS6GG25kT7wdsf66jJ1M /es/languages/php.js
|
|
||||||
sha384-VxmvZ2mUpp1EzFijS40RFvIc7vbv/d5PhMxVFG/3HMpVKD4sVvhdV9LThrJDiw9e /es/languages/php.min.js
|
|
||||||
sha384-Qin+c4lNK1aBTVV9uViy9uCeqaw3milnNPTq/TTWClWsc7ZBMi3kDc4d702bPhbf /es/languages/php-template.js
|
|
||||||
sha384-/Btu11Y1N5hp8EDB6nZJCif5GWMrSC/bYMh0Re/SH64ZoH2qZRHUtqv1SFRPbNdM /es/languages/php-template.min.js
|
|
||||||
sha384-i6sPjmXfHWLljAXTYYk0vBOwgsUnUKnKXKH41qzc9OMhaf5AFZqXH7azX4SYdUiR /es/languages/plaintext.js
|
|
||||||
sha384-OOrQLW97d+/1orj9gjftwbbQyV8LNAcgagqVKBhUYA08Hdi5w0p6VoB3yt2k7gnG /es/languages/plaintext.min.js
|
|
||||||
sha384-Cmq5lORXzyHraasLNqmfchH07JRXyEmjDF+j6tSggoXjYHwtgX/ySW6kkRytM5uu /es/languages/python.js
|
|
||||||
sha384-ZV5sgX70bBgLkDR5Mtox5UsbJedBc39hRKPdvTw6miK4lSkE/wv94cLY2iyZb/sB /es/languages/python.min.js
|
|
||||||
sha384-9jy7CI5TLYs91VYlZ4S0czQFpDsMzc9jakiJ5RqCFZugGpdPglP94JPAyEhMBwJp /es/languages/python-repl.js
|
|
||||||
sha384-CO+NiDedness48VlHW0FCIw9rQt8szFZ0IOXYiQo3LEAHLytXxM5NlKnmNYBUACQ /es/languages/python-repl.min.js
|
|
||||||
sha384-vAVurAb8AMWBKPHNWPze2lJa7m/as9wrF1wzH1FQKrJNNqiemBLJi3y6aGdyu5Zt /es/languages/r.js
|
|
||||||
sha384-WkipJgUBRzr12T7CtL8kF6QTuPHAl8f7Sx1YnmOm3KiVA/k3eDXfifuSvUW9M5nu /es/languages/r.min.js
|
|
||||||
sha384-JOe8PF7ynaYxu7HI5O0NuVfXMMXYSJlCJqP4TYVNNq0eDKgm/N2dqcmqvp9QfIDu /es/languages/ruby.js
|
|
||||||
sha384-DpXpbYSP6sX4tcP61ZRjSMsnmF8V3c/hQILWjrGWI2g3lresYaqbxVxs+tioFMJn /es/languages/ruby.min.js
|
|
||||||
sha384-JFRCn12yvr0NDhxPY8oZDk/G2Tjm7bGmqXy28Y0bq4J7D8mKha6jQJOXMB5wtTVr /es/languages/rust.js
|
|
||||||
sha384-JbkB8w/DGGyx29PIwSq8c/ZeiJB9T/X4mVAZFEyBiNlEAas98Q2NxpBPUlNIlE71 /es/languages/rust.min.js
|
|
||||||
sha384-R67rULqIohpEyV6aFbjxRv7xhK8v/KteX4cvOFmPcnZ2MTf65Zua+2DzB9MqqXuO /es/languages/scss.js
|
|
||||||
sha384-WMy5VYgOMFAnHhPJXVDCQ/Y/QPlUrBqNVPtFH6/gGg2F4uaAowyQ0y/9zWEeGpJe /es/languages/scss.min.js
|
|
||||||
sha384-1mmBZmAs44b6FtD9wpMiLJa8bLZgZv9xoAhngN6B5Q22y9CtcsU2lat0zlRtyVgy /es/languages/shell.js
|
|
||||||
sha384-u9PV7oWG/lZlm+GnftX7kn0w4b8rRfFxSv5SmJJPHWKGI03rz6VLqgZdQ1B5ez6b /es/languages/shell.min.js
|
|
||||||
sha384-s1ZfN6xtlNKAZux8QYAG7upUsit3RwK5XDoCAN3g6Kj33RrIqbmkuGjdNF9RvzPM /es/languages/sql.js
|
|
||||||
sha384-y25cn06synxhYnlKVprZdpakuFWVrm2jvn8pqiF4L85a05CI/6bNeT2+qXbUYIyW /es/languages/sql.min.js
|
|
||||||
sha384-sfRYvVvcwsysqkDUscQ/SqsFOSvNGkGX5vm/yKMHdTwTd7A++Pqx1QpJK0bGebPD /es/languages/swift.js
|
|
||||||
sha384-9NAaCxdhTO7TX6fYeUHyt+NC3ledirZOADyWdinDCTN1taeqj8sLLYqjE8YMf4Na /es/languages/swift.min.js
|
|
||||||
sha384-Z61gsCS2W7Q+3fT1fdya/Sz4wlmsotT9iEGzgIlNqP0soaKH1NzysutxWp08Hh3E /es/languages/typescript.js
|
|
||||||
sha384-Tv4mr9B7b+x2IynRXW/xcAxUj1+AoN9zyp0n9BWE1Nle3Zfm/zUeEztNLhIRjgE7 /es/languages/typescript.min.js
|
|
||||||
sha384-ZXhYu2xIPf1aci2kiX/n0hceCz4WwbJJs3QRE6ZwDXGzHRFWrsaYiQhBQSZ3Sx9K /es/languages/vbnet.js
|
|
||||||
sha384-ztGjA/YtvuFeKJwRqtrvV+3no91MVzQIzG5kQhHiIndfxmQOAum+vaaaDfA8Mg8U /es/languages/vbnet.min.js
|
|
||||||
sha384-82eHXc3kQTsEJ65AcO2c8eVqB9ynJzosSiMwdPYwt5oNRVsMKuxWoWkO5KFekVYB /es/languages/wasm.js
|
|
||||||
sha384-YBbT3eXpwj5Ddx0MS774iycYICw4gZ1Rs1ExYGIdcYC4EJhaWsfd9uNlaJZBuDaa /es/languages/wasm.min.js
|
|
||||||
sha384-9ECFzM+oWDye4s/MFx3QUXGo4mW43+SyLpWUDeQtWup6GZJ+KHFxVS89PmZt/fzl /es/languages/xml.js
|
|
||||||
sha384-PQrsaWeWrBiE1CFRw8K335CaJuQRTjDGm73vn8bXvlwaw6RyqWObdvMTBS8B75NN /es/languages/xml.min.js
|
|
||||||
sha384-7HTgKp/l2rzlyrh5vUfbfZVy+Wx1lKO4iGmfqvakienApv21u55lo+Vi+iVg4jY0 /es/languages/yaml.js
|
|
||||||
sha384-4smueUtgWTorlNLbaQIawnVCcIAuw82NetPOGWN5PbZT/pMr0rjvZXj0EUzJV1nr /es/languages/yaml.min.js
|
|
||||||
sha384-Jrkpn2hK0TY04skYBXB9fj7mMpKYy7g726cPwXGXf6mdBXnFlTDXFduxikMCRXT7 /languages/bash.js
|
|
||||||
sha384-BbT8tZtvkh8HPXIvL5RtzuljBwI3gR5KIdYxZyYSyI5C/+KNAGdzAiexvmxuroag /languages/bash.min.js
|
|
||||||
sha384-lAz0Eyld5DmFJB7cxaZonrkUJzGefh+K3niV5d7+vzzS7/P7FEeCJeLNXzMjeo+N /languages/c.js
|
|
||||||
sha384-tMmX0hBMZeMrZhX6dUNxA94/DNJLl70ao6qu2N9+b/6Ep9Y2e1pBzVjxtLygIB+d /languages/c.min.js
|
|
||||||
sha384-Z5Ja/rxBluJ4iPYwJYn2numfw2XFmlp3qLL1aJ1SZqyTjKWwMh9yWfpNCOqf3vAm /languages/cpp.js
|
|
||||||
sha384-B711MHXDqRvH/pKkxJk84RyRt9g0qyAJFsu2XukZKoCdnEiBmA6Aq9fO23ZCS7qk /languages/cpp.min.js
|
|
||||||
sha384-NTF0oluJbKDCxwGTujk+IsRQRbf+waUyDilA5GhOA+VSoxhyApQpmDWMjxfFO3dt /languages/csharp.js
|
|
||||||
sha384-Z+o7SU/ldIEIdOIqpMV+9s2n8EE1rZTFSRv5Sd7rlaSoPTpyflmmZ/oRb6ycw/2s /languages/csharp.min.js
|
|
||||||
sha384-bsb3QmLtUiyaiHwtrL4YoAVI9yLsjyqxgoAsk4Zd+ass9rSK1WWRiCDSu/hm8QRp /languages/css.js
|
|
||||||
sha384-0XGvxIU7Oq1DQMMBr1ORiozzBq3KpZPE/74mJysWRBAop1dZ9Ioq/qRWe8u30Ded /languages/css.min.js
|
|
||||||
sha384-UZBiDq19/Pu+BEZTOdnKdnew0sCWKFa2EmtRr9O+ZndYF1NgJOlya5bua3Wf++BW /languages/diff.js
|
|
||||||
sha384-04MxX6iQ0WrwX6Df4GJWGCXwfr5hVS5CQ0r9CS7aunho7Fkj/AAWbEPU8a6G+4LA /languages/diff.min.js
|
|
||||||
sha384-uh0SMHiaDpf+y9t0NE6+t9c3aMlzs8mHPxmkEU3fY12P481V2wNrKoxPMlKVG61k /languages/go.js
|
|
||||||
sha384-HdearVH8cyfzwBIQOjL/6dSEmZxQ5rJRezN7spps8E7iu+R6utS8c2ab0AgBNFfH /languages/go.min.js
|
|
||||||
sha384-IOWs5jCSdfqtJw3g+55axGoOxl+91x7BjVqyS+nhmIO3riBINecgkX3IyhdIjNQB /languages/graphql.js
|
|
||||||
sha384-M04f/a+xFV20/v8ZQLe5lPeqUKrH0A0h6HUSWFRvq4RE4xlU1yaJIE5XqNSuR2Ke /languages/graphql.min.js
|
|
||||||
sha384-izwcylRNWmKKRcyCyrYZyNQekUCyR7Fh1x8nYWNCRJoRDU5JXv6TcqlP4C+4MfIf /languages/ini.js
|
|
||||||
sha384-NrmnsdarwteQHPGjt70kaQTi1KE0XfOJNX9/VJSg6wWwU6U2nPzjl3iWkgU1cvyx /languages/ini.min.js
|
|
||||||
sha384-Dprg6CdFFkimxaHg7qM7njVaWLMlOLqughixPERBDbm0cHdX6rKujTnJReof8O6m /languages/java.js
|
|
||||||
sha384-e+59xEZvRMXSRGD31B3HOBGAGqhhs+bbkxCqPuJDkSX5QGneIGTIfwdYJckTN3AO /languages/java.min.js
|
|
||||||
sha384-yxv7Fv9ToggiLsR67t98hV5ZRup6XX6xL1Rkbi/cGV5J8y7fosCi9POqlBkiBWFg /languages/javascript.js
|
|
||||||
sha384-tPOrIubtDHoQU7Rqw0o88ilthGO0/4xEZGB47XrQKWhrc1/SchwsDx+AP74u4nk0 /languages/javascript.min.js
|
|
||||||
sha384-pUlqdjoNePvHvdi7GVKJJnh/P2T3EvXXodl5j0JtTkbNC4DRH7gwGbcHFa84bFOP /languages/json.js
|
|
||||||
sha384-3C+cPClJZgjKFYAb0bh35D7im2jasLzgk9eRix3t1c5pk1+x6b+bHghWcdrKwIo3 /languages/json.min.js
|
|
||||||
sha384-vIyPs+G4S+ut5NV5tBIN5/E17wBiWbTTkFPPFbBC+r/FZOD95/fbcSzzeo00bE3x /languages/kotlin.js
|
|
||||||
sha384-7abn027YsNDPFilpW9aLlNUanPrq7Ht81zKQL9MKQq6/nkKrLczChRK5OA8GSKep /languages/kotlin.min.js
|
|
||||||
sha384-KSqRjSg7Nn1FuuRUtjB7br82XVgWtqos5H9BlvRY1j5YQr2lftIUSg5deukqK89p /languages/less.js
|
|
||||||
sha384-M7Wfa4KRyfGnn1i52Nqpr5zWcrmVaO0luxCB+2Txz1eI2FRQfpDcNimn1f86K2Cp /languages/less.min.js
|
|
||||||
sha384-IQZHDTDQQ0zpXf1FfEYOFIfjZrBbLbNXYCn4zukU6u9mLf6JI36vvIRaV6/d175T /languages/lua.js
|
|
||||||
sha384-dbTI+BVfiAlIfjWMYrH83f/x/GYSKbujaX4g4F7q5YxbGtlS7qTLcwBQQvDdsGf6 /languages/lua.min.js
|
|
||||||
sha384-NpIMNHXY0x67yhJSnXiHh9V28uT0Bfz2cKxc30p9vURMu5IAcDhJT1TpaqUE1x0B /languages/makefile.js
|
|
||||||
sha384-DjL90zP08vzabGXs0CglFocqoxPXnzAcKhobGV+CQDA5FHGW7xCnxjylOhna+HB+ /languages/makefile.min.js
|
|
||||||
sha384-Sk9XW/OOutdl6KS1M9Wson0imuqr0LkpoTRDHi5QFH4MWe0aViI5d86BOVkh8Ds0 /languages/markdown.js
|
|
||||||
sha384-Rv26WbhHH4MDPzeExq4ECmZUYF942tlfVhqA91Drw1P+Ey55KjihLF9RJENxjWr1 /languages/markdown.min.js
|
|
||||||
sha384-hpU4KjKsUFgTYugJheYLkhFIEvecxLYra9Fg0ptjxqCxlUyMCJirJD/2IQDjZihD /languages/objectivec.js
|
|
||||||
sha384-azcLq84HapvEpXsDDJ2m1n7KovejGjCdGV4Ilw9xlcb6Yg2EyGNVr5dHZyoLdVDw /languages/objectivec.min.js
|
|
||||||
sha384-qLoCYnNDldQrhnuTfd5BAc54A/ulhuQYKYDYiU+iJRa87k5owWYLvnL0ttLWmFKQ /languages/perl.js
|
|
||||||
sha384-PK5CVcMiWQ08dZFregTL56n1urRNEsSuWT6oiH1sFm/2ac/epI35hC5lx+YzjH5U /languages/perl.min.js
|
|
||||||
sha384-0XBmTxpMLuDjB2zdfbi3Lv4Yokm2e1YFGZ9mCmI5887Kpi23jMF5N7rPrf0GdoU/ /languages/php.js
|
|
||||||
sha384-Bv/Sxv6HlOzYOdV1iQpJTG3xiqGWIIMq9xsFfEX8ss7oNWMgKqOa/J2WSFG2m7Jd /languages/php.min.js
|
|
||||||
sha384-DQroZ14Erpo7ay5JoNeZEUe41UI7w0Jra2nABCsZVG/EJVO5Zfb2sS1fEt/YGGPe /languages/php-template.js
|
|
||||||
sha384-LIzUVMUAZRreWHTENKQ/wXuNK17VO4xPf+kR1a0aBKvM3S7vsedCcFJZC7N7vdDt /languages/php-template.min.js
|
|
||||||
sha384-MZKv9uidO1+VnHz8xWxPv6ACuLO5t823eanvTcKYnmi/ocdVYD8zKZNTxmF0nKEM /languages/plaintext.js
|
|
||||||
sha384-Z9EdtPaC8UiXHEq1WuQTdvqT+FwjLwaVTIwTCZW/dGfiU9nbF8Shvltrhqtw83Qb /languages/plaintext.min.js
|
|
||||||
sha384-ueSSFZFqg7cVD0dpEqIk9EefJiJUYan0PH6I8u/p+bNLLx7dMs4J2keMaFXqCN8P /languages/python.js
|
|
||||||
sha384-eXRt+aAa2ig1yFVDQCLis8k9s/1dikTcigj+/R07yNdIxc8BAG/b1uHDyEW3of17 /languages/python.min.js
|
|
||||||
sha384-Zr5t2YaLU0giGFY/MuBA8UrK47JGpd9DuryiosYFRSQ6EJfTIF9mt8IJp/4/hpOU /languages/python-repl.js
|
|
||||||
sha384-n3iFvvEGhuJlnYpLj6JaCg7WiOhd8kQfKTZBDnpJwFZ0puRMhoq9JWtKDRw5Snyq /languages/python-repl.min.js
|
|
||||||
sha384-Wnn+wk2C8+hIgCNyvLC1dFTsN01c/yrpVqpKDNYF1M0Rg5kYCCmO8o8lS/yY3w2D /languages/r.js
|
|
||||||
sha384-poz1JEq/ihCmK+1p8IXPoluMlou+rnb+4Q3DenGB+mkNBB/JZkT0c/TERX8D+RUX /languages/r.min.js
|
|
||||||
sha384-6rhZe8x0LGCtYYrvHFTyO9QfZq2jHdoFsruI9B+lvUD0+Gc2Bn4JW0+cEC94ly3c /languages/ruby.js
|
|
||||||
sha384-Rlnlnjp0sedK9HVa29DtCyVFVEDRZyeTMQ6+aOKUaXptJmpVGTEmCk6ziXfmku6l /languages/ruby.min.js
|
|
||||||
sha384-4hMItQrXDnquJWRbDiZ+cP4udu1pcJlCVFg3Ytv9OgWNbpIwzizsWbIwzA1BAJrI /languages/rust.js
|
|
||||||
sha384-kENps59cKQW5DV3vOEzpSp6tfGzWGpPYKz748i4gGziVSjieRtupNNu/WEwG3s8n /languages/rust.min.js
|
|
||||||
sha384-e5MJZgawCv4c+BexmFUMVQU6dLcIOXdieG/a1FPCIgnlGfBIEUUcFMMo+UrKMOtN /languages/scss.js
|
|
||||||
sha384-BYdYy4D3IX6eNNlKqsviUjxC6EqavvNwCVDMzmie3QXyArWdCQf+VvvFo4ciaNaW /languages/scss.min.js
|
|
||||||
sha384-BanM6jNzM3hgNw0Vu3gSe58a3MK3PSlMUzh5s8QaaDzIvTWgB0jNetV3rNxteKHy /languages/shell.js
|
|
||||||
sha384-mSZF08WaP0Llc4GMwE0KA2V9yfZQimO5PvfcXf2AATDdqri3Q7IdV7pfbhVPJCHV /languages/shell.min.js
|
|
||||||
sha384-2sXmcW3eKeNDWiLtuq9NgFJC4NsLBN/fDTzZevmcgBrSERv6iO/k+c7r9T09Fb8J /languages/sql.js
|
|
||||||
sha384-jrnLoVn13sB+/dTfoAYVPhg0tYGQzzuzSGP3WTk8OvKAY0hDejpUXFYYI3bohAyW /languages/sql.min.js
|
|
||||||
sha384-+juhAXbxlgltos7eNuzta0Y7hfKqGQftMcEEStYqBJftSEdIiLd/FaviI8hs4d86 /languages/swift.js
|
|
||||||
sha384-CCauhmYx0fwWViYO6uiTII5shLTfiY/OzxKmLRTeCp8Ok81I2nXZS2Gb9lJVOSPC /languages/swift.min.js
|
|
||||||
sha384-8v3YMaXFO9cmTNxsHWqwn9wJsV1jVO7rwx4huxqlEQpT/P2tuDbtm+Hs0EdYqu0a /languages/typescript.js
|
|
||||||
sha384-df1w1nJ43GNwmgbSCrT8YFIYyqFAm+lzj+b6ofuziX8Cfdg9QHFwbORDgAaj//wi /languages/typescript.min.js
|
|
||||||
sha384-PWtej+1fbEACjPO/+i55ybZvKUn+nUtSRAkFKLQx6O4zrFbiIUlmnErVVh6oZAxa /languages/vbnet.js
|
|
||||||
sha384-9jaz2rSOFx5kYjZB+Loaf0a4ipf9Yvk08+8QskyozD+yaHdA14SgQKv0C52/UooX /languages/vbnet.min.js
|
|
||||||
sha384-TCN/hvup/XKpDtGmR/RyK6NSG247wkNROUpO2sAoJuwpMvcr4KP9HA+K5L2rvOKg /languages/wasm.js
|
|
||||||
sha384-J3pUKFGnHJH0czAle+lKF96F/08caYKJfTEzlt5dGbGTR9M4BwOeOqAgvSzsjOsP /languages/wasm.min.js
|
|
||||||
sha384-Pgzg6a405W6U1xFjjSs5i8d7V81Tmt/TYn8HFOa+u1psDc8cbs8nC7BuyNXbWWRK /languages/xml.js
|
|
||||||
sha384-FQjSArDMJE4WMAJGcCNAV+IXIOljcIxM3UFAD2vxjedWmBnnDaAyqRG7AQHf/uM/ /languages/xml.min.js
|
|
||||||
sha384-6GXi9L5BnOWPU6bzwYL78Zscp23qyDdMLZpZvp4mLzvF2qt0eY/DfsPHiFVXq4hv /languages/yaml.js
|
|
||||||
sha384-A/iMReLA0Bo3tLydBIoOQXQzYnrwL90jkHYUubrtERUGCbIuU7U0EHge0Xd2s5sr /languages/yaml.min.js
|
|
||||||
sha384-5ka3JsbfeTzh/0oDZbLvQwZvfZF6OxAjEYtEbOSTzc/Hr3cowRFHlvRVl9BEVkFr /highlight.js
|
|
||||||
sha384-DfuUw42ioE+mBJmUkFRnFbaafZ8+pICt6pnhycbXgWm4QiFC0B22CJt6WcvWs/40 /highlight.min.js
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
BSD 3-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2006, Ivan Sagalaev.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the copyright holder nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# Highlight.js CDN Assets
|
|
||||||
|
|
||||||
[](https://packagephobia.now.sh/result?p=highlight.js)
|
|
||||||
|
|
||||||
**This package contains only the CDN build assets of highlight.js.**
|
|
||||||
|
|
||||||
This may be what you want if you'd like to install the pre-built distributable highlight.js client-side assets via NPM. If you're wanting to use highlight.js mainly on the server-side you likely want the [highlight.js][1] package instead.
|
|
||||||
|
|
||||||
To access these files via CDN:<br>
|
|
||||||
https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/
|
|
||||||
|
|
||||||
**If you just want a single .js file with the common languages built-in:
|
|
||||||
<https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@latest/build/highlight.min.js>**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Highlight.js
|
|
||||||
|
|
||||||
Highlight.js is a syntax highlighter written in JavaScript. It works in
|
|
||||||
the browser as well as on the server. It works with pretty much any
|
|
||||||
markup, doesn’t depend on any framework, and has automatic language
|
|
||||||
detection.
|
|
||||||
|
|
||||||
If you'd like to read the full README:<br>
|
|
||||||
<https://github.com/highlightjs/highlight.js/blob/main/README.md>
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Highlight.js is released under the BSD License. See [LICENSE][7] file
|
|
||||||
for details.
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
The official site for the library is at <https://highlightjs.org/>.
|
|
||||||
|
|
||||||
The Github project may be found at: <https://github.com/highlightjs/highlight.js>
|
|
||||||
|
|
||||||
Further in-depth documentation for the API and other topics is at
|
|
||||||
<http://highlightjs.readthedocs.io/>.
|
|
||||||
|
|
||||||
A list of the Core Team and contributors can be found in the [CONTRIBUTORS.md][8] file.
|
|
||||||
|
|
||||||
[1]: https://www.npmjs.com/package/highlight.js
|
|
||||||
[7]: https://github.com/highlightjs/highlight.js/blob/main/LICENSE
|
|
||||||
[8]: https://github.com/highlightjs/highlight.js/blob/main/CONTRIBUTORS.md
|
|
||||||
File diff suppressed because it is too large
Load Diff
-306
@@ -1,306 +0,0 @@
|
|||||||
/*!
|
|
||||||
Highlight.js v11.11.1 (git: 08cb242e7d)
|
|
||||||
(c) 2006-2025 Josh Goebel <hello@joshgoebel.com> and other contributors
|
|
||||||
License: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
function e(t){return t instanceof Map?t.clear=t.delete=t.set=()=>{
|
|
||||||
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
|
|
||||||
throw Error("set is read-only")
|
|
||||||
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
|
|
||||||
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
|
|
||||||
})),t}class t{constructor(e){
|
|
||||||
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
|
|
||||||
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
|
|
||||||
return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")
|
|
||||||
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
|
|
||||||
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
|
|
||||||
;class r{constructor(e,t){
|
|
||||||
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
|
|
||||||
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
|
|
||||||
if(e.startsWith("language:"))return e.replace("language:","language-")
|
|
||||||
;if(e.includes(".")){const n=e.split(".")
|
|
||||||
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
|
|
||||||
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
|
|
||||||
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
|
|
||||||
this.buffer+=`<span class="${e}">`}}const o=(e={})=>{const t={children:[]}
|
|
||||||
;return Object.assign(t,e),t};class a{constructor(){
|
|
||||||
this.rootNode=o(),this.stack=[this.rootNode]}get top(){
|
|
||||||
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
|
|
||||||
this.top.children.push(e)}openNode(e){const t=o({scope:e})
|
|
||||||
;this.add(t),this.stack.push(t)}closeNode(){
|
|
||||||
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
|
|
||||||
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
|
|
||||||
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
|
|
||||||
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
|
|
||||||
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
|
|
||||||
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
|
|
||||||
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
|
|
||||||
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
|
|
||||||
this.closeNode()}__addSublanguage(e,t){const n=e.root
|
|
||||||
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
|
|
||||||
return new r(this,this.options).value()}finalize(){
|
|
||||||
return this.closeAllNodes(),!0}}function l(e){
|
|
||||||
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
|
|
||||||
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
|
|
||||||
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
|
|
||||||
const t=e[e.length-1]
|
|
||||||
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
|
|
||||||
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
|
|
||||||
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
|
|
||||||
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
|
|
||||||
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
|
|
||||||
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
|
|
||||||
s+=i.substring(0,e.index),
|
|
||||||
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
|
|
||||||
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
|
|
||||||
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",_="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={
|
|
||||||
begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'",
|
|
||||||
illegal:"\\n",contains:[O]},v={scope:"string",begin:'"',end:'"',illegal:"\\n",
|
|
||||||
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
|
|
||||||
contains:[]},n);s.contains.push({scope:"doctag",
|
|
||||||
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
|
|
||||||
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
|
|
||||||
;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
|
|
||||||
;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
|
|
||||||
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var A=Object.freeze({
|
|
||||||
__proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
|
|
||||||
scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N,
|
|
||||||
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
|
|
||||||
begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
|
|
||||||
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
|
|
||||||
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
|
|
||||||
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
|
|
||||||
NUMBER_MODE:{scope:"number",begin:_,relevance:0},NUMBER_RE:_,
|
|
||||||
PHRASAL_WORDS_MODE:{
|
|
||||||
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
|
|
||||||
},QUOTE_STRING_MODE:v,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
|
|
||||||
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
|
|
||||||
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
|
|
||||||
SHEBANG:(e={})=>{const t=/^#![ ]*\//
|
|
||||||
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
|
|
||||||
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
|
|
||||||
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
|
|
||||||
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function j(e,t){
|
|
||||||
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
|
|
||||||
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
|
|
||||||
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
|
|
||||||
e.__beforeBegin=j,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
|
|
||||||
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
|
|
||||||
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
|
|
||||||
if(e.match){
|
|
||||||
if(e.begin||e.end)throw Error("begin & end are not supported with match")
|
|
||||||
;e.begin=e.match,delete e.match}}function P(e,t){
|
|
||||||
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
|
|
||||||
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
|
|
||||||
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
|
|
||||||
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
|
|
||||||
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
|
|
||||||
},e.relevance=0,delete n.beforeMatch
|
|
||||||
},H=["of","and","for","in","not","or","if","then","parent","list","value"]
|
|
||||||
;function C(e,t,n="keyword"){const i=Object.create(null)
|
|
||||||
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
|
|
||||||
Object.assign(i,C(e[n],t,n))})),i;function s(e,n){
|
|
||||||
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
|
|
||||||
;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){
|
|
||||||
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{
|
|
||||||
console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{
|
|
||||||
U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0)
|
|
||||||
},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={}
|
|
||||||
;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1])
|
|
||||||
;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{
|
|
||||||
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
|
|
||||||
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
|
|
||||||
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
|
|
||||||
}),(e=>{if(Array.isArray(e.begin)){
|
|
||||||
if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
|
|
||||||
G
|
|
||||||
;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"),
|
|
||||||
G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
|
|
||||||
if(Array.isArray(e.end)){
|
|
||||||
if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
|
|
||||||
G
|
|
||||||
;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"),
|
|
||||||
G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){
|
|
||||||
function t(t,n){
|
|
||||||
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
|
|
||||||
}class n{constructor(){
|
|
||||||
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
|
|
||||||
addRule(e,t){
|
|
||||||
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
|
|
||||||
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
|
|
||||||
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
|
|
||||||
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
|
|
||||||
;const t=this.matcherRe.exec(e);if(!t)return null
|
|
||||||
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
|
|
||||||
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
|
|
||||||
this.rules=[],this.multiRegexes=[],
|
|
||||||
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
|
|
||||||
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
|
|
||||||
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
|
|
||||||
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
|
|
||||||
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
|
|
||||||
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
|
|
||||||
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
|
|
||||||
;let n=t.exec(e)
|
|
||||||
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
|
|
||||||
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
|
|
||||||
return n&&(this.regexIndex+=n.position+1,
|
|
||||||
this.regexIndex===this.count&&this.considerAll()),n}}
|
|
||||||
if(e.compilerExtensions||(e.compilerExtensions=[]),
|
|
||||||
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
|
|
||||||
;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r
|
|
||||||
;if(r.isCompiled)return a
|
|
||||||
;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))),
|
|
||||||
r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null
|
|
||||||
;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords),
|
|
||||||
c=r.keywords.$pattern,
|
|
||||||
delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)),
|
|
||||||
a.keywordPatternRe=t(c,!0),
|
|
||||||
o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),
|
|
||||||
r.end&&(a.endRe=t(a.end)),
|
|
||||||
a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)),
|
|
||||||
r.illegal&&(a.illegalRe=t(r.illegal)),
|
|
||||||
r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
|
|
||||||
variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{
|
|
||||||
starts:e.starts?i(e.starts):null
|
|
||||||
}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a)
|
|
||||||
})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s
|
|
||||||
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
|
|
||||||
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
|
|
||||||
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){
|
|
||||||
return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{
|
|
||||||
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
|
|
||||||
const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{
|
|
||||||
const i=Object.create(null),s=Object.create(null),r=[];let o=!0
|
|
||||||
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
|
|
||||||
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
|
|
||||||
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
|
|
||||||
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
|
|
||||||
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
|
|
||||||
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
|
|
||||||
;"object"==typeof t?(i=e,
|
|
||||||
n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),
|
|
||||||
X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
|
|
||||||
s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r)
|
|
||||||
;const o=r.result?r.result:E(r.language,r.code,n)
|
|
||||||
;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){
|
|
||||||
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
|
|
||||||
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
|
|
||||||
;for(;t;){n+=R.substring(e,t.index)
|
|
||||||
;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){
|
|
||||||
const[e,i]=r
|
|
||||||
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(A+=i),e.startsWith("_"))n+=t[0];else{
|
|
||||||
const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
|
|
||||||
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
|
|
||||||
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
|
|
||||||
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
|
|
||||||
if(!i[N.subLanguage])return void M.addText(R)
|
|
||||||
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
|
|
||||||
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
|
|
||||||
;N.relevance>0&&(A+=e.relevance),M.__addSublanguage(e._emitter,e.language)
|
|
||||||
})():l(),R=""}function u(e,t){
|
|
||||||
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
|
|
||||||
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
|
|
||||||
const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
|
|
||||||
function h(e,t){
|
|
||||||
return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),
|
|
||||||
e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
|
|
||||||
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
|
|
||||||
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
|
|
||||||
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
|
|
||||||
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
|
|
||||||
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
|
|
||||||
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
|
|
||||||
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
|
|
||||||
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N
|
|
||||||
;N.endScope&&N.endScope._wrap?(g(),
|
|
||||||
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
|
|
||||||
d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t),
|
|
||||||
g(),r.excludeEnd&&(R=t));do{
|
|
||||||
N.scope&&M.closeNode(),N.skip||N.subLanguage||(A+=N.relevance),N=N.parent
|
|
||||||
}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length}
|
|
||||||
let _={};function y(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0
|
|
||||||
;if("begin"===_.type&&"end"===r.type&&_.index===r.index&&""===a){
|
|
||||||
if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`)
|
|
||||||
;throw t.languageName=e,t.badRule=_.rule,t}return 1}
|
|
||||||
if(_=r,"begin"===r.type)return(e=>{
|
|
||||||
const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]]
|
|
||||||
;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
|
|
||||||
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
|
|
||||||
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r)
|
|
||||||
;if("illegal"===r.type&&!s){
|
|
||||||
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
|
|
||||||
;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e}
|
|
||||||
if("illegal"===r.type&&""===a)return R+="\n",1
|
|
||||||
;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
|
|
||||||
;return R+=a,a.length}const w=O(e)
|
|
||||||
;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
|
|
||||||
;const k=Z(w);let v="",N=r||k;const S={},M=new p.__emitter(p);(()=>{const e=[]
|
|
||||||
;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope)
|
|
||||||
;e.forEach((e=>M.openNode(e)))})();let R="",A=0,j=0,I=0,T=!1;try{
|
|
||||||
if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
|
|
||||||
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=j
|
|
||||||
;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(j,e.index),e)
|
|
||||||
;j=e.index+t}y(n.substring(j))}return M.finalize(),v=M.toHTML(),{language:e,
|
|
||||||
value:v,relevance:A,illegal:!1,_emitter:M,_top:N}}catch(t){
|
|
||||||
if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n),
|
|
||||||
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j,
|
|
||||||
context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:v},_emitter:M};if(o)return{
|
|
||||||
language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
|
|
||||||
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
|
|
||||||
const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
|
|
||||||
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(v).map((t=>E(t,e,!1)))
|
|
||||||
;s.unshift(n);const r=s.sort(((e,t)=>{
|
|
||||||
if(e.relevance!==t.relevance)return t.relevance-e.relevance
|
|
||||||
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
|
|
||||||
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o
|
|
||||||
;return c.secondBest=a,c}function _(e){let t=null;const n=(e=>{
|
|
||||||
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
|
|
||||||
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
|
|
||||||
;return t||(W(a.replace("{}",n[1])),
|
|
||||||
W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
|
|
||||||
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
|
|
||||||
;if(N("before:highlightElement",{el:e,language:n
|
|
||||||
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
|
|
||||||
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
|
|
||||||
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
|
|
||||||
console.warn("The element with unescaped HTML:"),
|
|
||||||
console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML)
|
|
||||||
;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
|
|
||||||
;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
|
|
||||||
;e.classList.add("hljs"),e.classList.add("language-"+i)
|
|
||||||
})(e,n,r.language),e.result={language:r.language,re:r.relevance,
|
|
||||||
relevance:r.relevance},r.secondBest&&(e.secondBest={
|
|
||||||
language:r.secondBest.language,relevance:r.secondBest.relevance
|
|
||||||
}),N("after:highlightElement",{el:e,result:r,text:i})}let y=!1;function w(){
|
|
||||||
if("loading"===document.readyState)return y||window.addEventListener("DOMContentLoaded",(()=>{
|
|
||||||
w()}),!1),void(y=!0);document.querySelectorAll(p.cssSelector).forEach(_)}
|
|
||||||
function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
|
|
||||||
function k(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
|
|
||||||
s[e.toLowerCase()]=t}))}function v(e){const t=O(e)
|
|
||||||
;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{
|
|
||||||
e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w,
|
|
||||||
highlightElement:_,
|
|
||||||
highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"),
|
|
||||||
X("10.7.0","Please use highlightElement now."),_(e)),configure:e=>{p=Y(p,e)},
|
|
||||||
initHighlighting:()=>{
|
|
||||||
w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
|
|
||||||
initHighlightingOnLoad:()=>{
|
|
||||||
w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
|
|
||||||
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
|
|
||||||
if(z("Language definition for '{}' could not be registered.".replace("{}",e)),
|
|
||||||
!o)throw t;z(t),s=l}
|
|
||||||
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&k(s.aliases,{
|
|
||||||
languageName:e})},unregisterLanguage:e=>{delete i[e]
|
|
||||||
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
|
|
||||||
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:k,
|
|
||||||
autoDetection:v,inherit:Y,addPlugin:e=>{(e=>{
|
|
||||||
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
|
|
||||||
e["before:highlightBlock"](Object.assign({block:t.el},t))
|
|
||||||
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
|
|
||||||
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)},
|
|
||||||
removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{
|
|
||||||
o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h,
|
|
||||||
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
|
|
||||||
;for(const t in A)"object"==typeof A[t]&&e(A[t]);return Object.assign(n,A),n
|
|
||||||
},te=ee({});te.newInstance=()=>ee({});export{te as default};
|
|
||||||
File diff suppressed because it is too large
Load Diff
-306
@@ -1,306 +0,0 @@
|
|||||||
/*!
|
|
||||||
Highlight.js v11.11.1 (git: 08cb242e7d)
|
|
||||||
(c) 2006-2025 Josh Goebel <hello@joshgoebel.com> and other contributors
|
|
||||||
License: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
function e(t){return t instanceof Map?t.clear=t.delete=t.set=()=>{
|
|
||||||
throw Error("map is read-only")}:t instanceof Set&&(t.add=t.clear=t.delete=()=>{
|
|
||||||
throw Error("set is read-only")
|
|
||||||
}),Object.freeze(t),Object.getOwnPropertyNames(t).forEach((n=>{
|
|
||||||
const i=t[n],s=typeof i;"object"!==s&&"function"!==s||Object.isFrozen(i)||e(i)
|
|
||||||
})),t}class t{constructor(e){
|
|
||||||
void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}
|
|
||||||
ignoreMatch(){this.isMatchIgnored=!0}}function n(e){
|
|
||||||
return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")
|
|
||||||
}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t]
|
|
||||||
;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const s=e=>!!e.scope
|
|
||||||
;class r{constructor(e,t){
|
|
||||||
this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){
|
|
||||||
this.buffer+=n(e)}openNode(e){if(!s(e))return;const t=((e,{prefix:t})=>{
|
|
||||||
if(e.startsWith("language:"))return e.replace("language:","language-")
|
|
||||||
;if(e.includes(".")){const n=e.split(".")
|
|
||||||
;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")
|
|
||||||
}return`${t}${e}`})(e.scope,{prefix:this.classPrefix});this.span(t)}
|
|
||||||
closeNode(e){s(e)&&(this.buffer+="</span>")}value(){return this.buffer}span(e){
|
|
||||||
this.buffer+=`<span class="${e}">`}}const o=(e={})=>{const t={children:[]}
|
|
||||||
;return Object.assign(t,e),t};class a{constructor(){
|
|
||||||
this.rootNode=o(),this.stack=[this.rootNode]}get top(){
|
|
||||||
return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){
|
|
||||||
this.top.children.push(e)}openNode(e){const t=o({scope:e})
|
|
||||||
;this.add(t),this.stack.push(t)}closeNode(){
|
|
||||||
if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){
|
|
||||||
for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}
|
|
||||||
walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){
|
|
||||||
return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),
|
|
||||||
t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){
|
|
||||||
"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{
|
|
||||||
a._collapse(e)})))}}class c extends a{constructor(e){super(),this.options=e}
|
|
||||||
addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){
|
|
||||||
this.closeNode()}__addSublanguage(e,t){const n=e.root
|
|
||||||
;t&&(n.scope="language:"+t),this.add(n)}toHTML(){
|
|
||||||
return new r(this,this.options).value()}finalize(){
|
|
||||||
return this.closeAllNodes(),!0}}function l(e){
|
|
||||||
return e?"string"==typeof e?e:e.source:null}function g(e){return h("(?=",e,")")}
|
|
||||||
function u(e){return h("(?:",e,")*")}function d(e){return h("(?:",e,")?")}
|
|
||||||
function h(...e){return e.map((e=>l(e))).join("")}function f(...e){const t=(e=>{
|
|
||||||
const t=e[e.length-1]
|
|
||||||
;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}
|
|
||||||
})(e);return"("+(t.capture?"":"?:")+e.map((e=>l(e))).join("|")+")"}
|
|
||||||
function p(e){return RegExp(e.toString()+"|").exec("").length-1}
|
|
||||||
const b=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./
|
|
||||||
;function m(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n
|
|
||||||
;let i=l(e),s="";for(;i.length>0;){const e=b.exec(i);if(!e){s+=i;break}
|
|
||||||
s+=i.substring(0,e.index),
|
|
||||||
i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?s+="\\"+(Number(e[1])+t):(s+=e[0],
|
|
||||||
"("===e[0]&&n++)}return s})).map((e=>`(${e})`)).join(t)}
|
|
||||||
const E="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",_="\\b\\d+(\\.\\d+)?",y="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",O={
|
|
||||||
begin:"\\\\[\\s\\S]",relevance:0},k={scope:"string",begin:"'",end:"'",
|
|
||||||
illegal:"\\n",contains:[O]},v={scope:"string",begin:'"',end:'"',illegal:"\\n",
|
|
||||||
contains:[O]},N=(e,t,n={})=>{const s=i({scope:"comment",begin:e,end:t,
|
|
||||||
contains:[]},n);s.contains.push({scope:"doctag",
|
|
||||||
begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",
|
|
||||||
end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0})
|
|
||||||
;const r=f("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/)
|
|
||||||
;return s.contains.push({begin:h(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),s
|
|
||||||
},S=N("//","$"),M=N("/\\*","\\*/"),R=N("#","$");var A=Object.freeze({
|
|
||||||
__proto__:null,APOS_STRING_MODE:k,BACKSLASH_ESCAPE:O,BINARY_NUMBER_MODE:{
|
|
||||||
scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:N,
|
|
||||||
C_BLOCK_COMMENT_MODE:M,C_LINE_COMMENT_MODE:S,C_NUMBER_MODE:{scope:"number",
|
|
||||||
begin:y,relevance:0},C_NUMBER_RE:y,END_SAME_AS_BEGIN:e=>Object.assign(e,{
|
|
||||||
"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{
|
|
||||||
t.data._beginMatch!==e[1]&&t.ignoreMatch()}}),HASH_COMMENT_MODE:R,IDENT_RE:E,
|
|
||||||
MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+x,relevance:0},
|
|
||||||
NUMBER_MODE:{scope:"number",begin:_,relevance:0},NUMBER_RE:_,
|
|
||||||
PHRASAL_WORDS_MODE:{
|
|
||||||
begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/
|
|
||||||
},QUOTE_STRING_MODE:v,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/,
|
|
||||||
end:/\/[gimuy]*/,contains:[O,{begin:/\[/,end:/\]/,relevance:0,contains:[O]}]},
|
|
||||||
RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
|
|
||||||
SHEBANG:(e={})=>{const t=/^#![ ]*\//
|
|
||||||
;return e.binary&&(e.begin=h(t,/.*\b/,e.binary,/\b.*/)),i({scope:"meta",begin:t,
|
|
||||||
end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},
|
|
||||||
TITLE_MODE:{scope:"title",begin:E,relevance:0},UNDERSCORE_IDENT_RE:x,
|
|
||||||
UNDERSCORE_TITLE_MODE:{scope:"title",begin:x,relevance:0}});function j(e,t){
|
|
||||||
"."===e.input[e.index-1]&&t.ignoreMatch()}function I(e,t){
|
|
||||||
void 0!==e.className&&(e.scope=e.className,delete e.className)}function T(e,t){
|
|
||||||
t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
|
|
||||||
e.__beforeBegin=j,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
|
|
||||||
void 0===e.relevance&&(e.relevance=0))}function L(e,t){
|
|
||||||
Array.isArray(e.illegal)&&(e.illegal=f(...e.illegal))}function B(e,t){
|
|
||||||
if(e.match){
|
|
||||||
if(e.begin||e.end)throw Error("begin & end are not supported with match")
|
|
||||||
;e.begin=e.match,delete e.match}}function P(e,t){
|
|
||||||
void 0===e.relevance&&(e.relevance=1)}const D=(e,t)=>{if(!e.beforeMatch)return
|
|
||||||
;if(e.starts)throw Error("beforeMatch cannot be used with starts")
|
|
||||||
;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]
|
|
||||||
})),e.keywords=n.keywords,e.begin=h(n.beforeMatch,g(n.begin)),e.starts={
|
|
||||||
relevance:0,contains:[Object.assign(n,{endsParent:!0})]
|
|
||||||
},e.relevance=0,delete n.beforeMatch
|
|
||||||
},H=["of","and","for","in","not","or","if","then","parent","list","value"]
|
|
||||||
;function C(e,t,n="keyword"){const i=Object.create(null)
|
|
||||||
;return"string"==typeof e?s(n,e.split(" ")):Array.isArray(e)?s(n,e):Object.keys(e).forEach((n=>{
|
|
||||||
Object.assign(i,C(e[n],t,n))})),i;function s(e,n){
|
|
||||||
t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
|
|
||||||
;i[n[0]]=[e,$(n[0],n[1])]}))}}function $(e,t){
|
|
||||||
return t?Number(t):(e=>H.includes(e.toLowerCase()))(e)?0:1}const U={},z=e=>{
|
|
||||||
console.error(e)},W=(e,...t)=>{console.log("WARN: "+e,...t)},X=(e,t)=>{
|
|
||||||
U[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),U[`${e}/${t}`]=!0)
|
|
||||||
},G=Error();function K(e,t,{key:n}){let i=0;const s=e[n],r={},o={}
|
|
||||||
;for(let e=1;e<=t.length;e++)o[e+i]=s[e],r[e+i]=!0,i+=p(t[e-1])
|
|
||||||
;e[n]=o,e[n]._emit=r,e[n]._multi=!0}function F(e){(e=>{
|
|
||||||
e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,
|
|
||||||
delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={
|
|
||||||
_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope
|
|
||||||
}),(e=>{if(Array.isArray(e.begin)){
|
|
||||||
if(e.skip||e.excludeBegin||e.returnBegin)throw z("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),
|
|
||||||
G
|
|
||||||
;if("object"!=typeof e.beginScope||null===e.beginScope)throw z("beginScope must be object"),
|
|
||||||
G;K(e,e.begin,{key:"beginScope"}),e.begin=m(e.begin,{joinWith:""})}})(e),(e=>{
|
|
||||||
if(Array.isArray(e.end)){
|
|
||||||
if(e.skip||e.excludeEnd||e.returnEnd)throw z("skip, excludeEnd, returnEnd not compatible with endScope: {}"),
|
|
||||||
G
|
|
||||||
;if("object"!=typeof e.endScope||null===e.endScope)throw z("endScope must be object"),
|
|
||||||
G;K(e,e.end,{key:"endScope"}),e.end=m(e.end,{joinWith:""})}})(e)}function Z(e){
|
|
||||||
function t(t,n){
|
|
||||||
return RegExp(l(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))
|
|
||||||
}class n{constructor(){
|
|
||||||
this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
|
|
||||||
addRule(e,t){
|
|
||||||
t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),
|
|
||||||
this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null)
|
|
||||||
;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(m(e,{joinWith:"|"
|
|
||||||
}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex
|
|
||||||
;const t=this.matcherRe.exec(e);if(!t)return null
|
|
||||||
;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n]
|
|
||||||
;return t.splice(0,n),Object.assign(t,i)}}class s{constructor(){
|
|
||||||
this.rules=[],this.multiRegexes=[],
|
|
||||||
this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){
|
|
||||||
if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n
|
|
||||||
;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),
|
|
||||||
t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){
|
|
||||||
return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){
|
|
||||||
this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){
|
|
||||||
const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex
|
|
||||||
;let n=t.exec(e)
|
|
||||||
;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{
|
|
||||||
const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}
|
|
||||||
return n&&(this.regexIndex+=n.position+1,
|
|
||||||
this.regexIndex===this.count&&this.considerAll()),n}}
|
|
||||||
if(e.compilerExtensions||(e.compilerExtensions=[]),
|
|
||||||
e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.")
|
|
||||||
;return e.classNameAliases=i(e.classNameAliases||{}),function n(r,o){const a=r
|
|
||||||
;if(r.isCompiled)return a
|
|
||||||
;[I,B,F,D].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))),
|
|
||||||
r.__beforeBegin=null,[T,L,P].forEach((e=>e(r,o))),r.isCompiled=!0;let c=null
|
|
||||||
;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords),
|
|
||||||
c=r.keywords.$pattern,
|
|
||||||
delete r.keywords.$pattern),c=c||/\w+/,r.keywords&&(r.keywords=C(r.keywords,e.case_insensitive)),
|
|
||||||
a.keywordPatternRe=t(c,!0),
|
|
||||||
o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(a.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),
|
|
||||||
r.end&&(a.endRe=t(a.end)),
|
|
||||||
a.terminatorEnd=l(a.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)),
|
|
||||||
r.illegal&&(a.illegalRe=t(r.illegal)),
|
|
||||||
r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>i(e,{
|
|
||||||
variants:null},t)))),e.cachedVariants?e.cachedVariants:V(e)?i(e,{
|
|
||||||
starts:e.starts?i(e.starts):null
|
|
||||||
}):Object.isFrozen(e)?i(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a)
|
|
||||||
})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new s
|
|
||||||
;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
|
|
||||||
}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
|
|
||||||
}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function V(e){
|
|
||||||
return!!e&&(e.endsWithParent||V(e.starts))}class q extends Error{
|
|
||||||
constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}
|
|
||||||
const J=n,Y=i,Q=Symbol("nomatch"),ee=n=>{
|
|
||||||
const i=Object.create(null),s=Object.create(null),r=[];let o=!0
|
|
||||||
;const a="Could not find the language '{}', did you forget to load/include a language module?",l={
|
|
||||||
disableAutodetect:!0,name:"Plain text",contains:[]};let p={
|
|
||||||
ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,
|
|
||||||
languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
|
|
||||||
cssSelector:"pre code",languages:null,__emitter:c};function b(e){
|
|
||||||
return p.noHighlightRe.test(e)}function m(e,t,n){let i="",s=""
|
|
||||||
;"object"==typeof t?(i=e,
|
|
||||||
n=t.ignoreIllegals,s=t.language):(X("10.7.0","highlight(lang, code, ...args) has been deprecated."),
|
|
||||||
X("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),
|
|
||||||
s=e,i=t),void 0===n&&(n=!0);const r={code:i,language:s};N("before:highlight",r)
|
|
||||||
;const o=r.result?r.result:E(r.language,r.code,n)
|
|
||||||
;return o.code=r.code,N("after:highlight",o),o}function E(e,n,s,r){
|
|
||||||
const c=Object.create(null);function l(){if(!N.keywords)return void M.addText(R)
|
|
||||||
;let e=0;N.keywordPatternRe.lastIndex=0;let t=N.keywordPatternRe.exec(R),n=""
|
|
||||||
;for(;t;){n+=R.substring(e,t.index)
|
|
||||||
;const s=w.case_insensitive?t[0].toLowerCase():t[0],r=(i=s,N.keywords[i]);if(r){
|
|
||||||
const[e,i]=r
|
|
||||||
;if(M.addText(n),n="",c[s]=(c[s]||0)+1,c[s]<=7&&(A+=i),e.startsWith("_"))n+=t[0];else{
|
|
||||||
const n=w.classNameAliases[e]||e;u(t[0],n)}}else n+=t[0]
|
|
||||||
;e=N.keywordPatternRe.lastIndex,t=N.keywordPatternRe.exec(R)}var i
|
|
||||||
;n+=R.substring(e),M.addText(n)}function g(){null!=N.subLanguage?(()=>{
|
|
||||||
if(""===R)return;let e=null;if("string"==typeof N.subLanguage){
|
|
||||||
if(!i[N.subLanguage])return void M.addText(R)
|
|
||||||
;e=E(N.subLanguage,R,!0,S[N.subLanguage]),S[N.subLanguage]=e._top
|
|
||||||
}else e=x(R,N.subLanguage.length?N.subLanguage:null)
|
|
||||||
;N.relevance>0&&(A+=e.relevance),M.__addSublanguage(e._emitter,e.language)
|
|
||||||
})():l(),R=""}function u(e,t){
|
|
||||||
""!==e&&(M.startScope(t),M.addText(e),M.endScope())}function d(e,t){let n=1
|
|
||||||
;const i=t.length-1;for(;n<=i;){if(!e._emit[n]){n++;continue}
|
|
||||||
const i=w.classNameAliases[e[n]]||e[n],s=t[n];i?u(s,i):(R=s,l(),R=""),n++}}
|
|
||||||
function h(e,t){
|
|
||||||
return e.scope&&"string"==typeof e.scope&&M.openNode(w.classNameAliases[e.scope]||e.scope),
|
|
||||||
e.beginScope&&(e.beginScope._wrap?(u(R,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),
|
|
||||||
R=""):e.beginScope._multi&&(d(e.beginScope,t),R="")),N=Object.create(e,{parent:{
|
|
||||||
value:N}}),N}function f(e,n,i){let s=((e,t)=>{const n=e&&e.exec(t)
|
|
||||||
;return n&&0===n.index})(e.endRe,i);if(s){if(e["on:end"]){const i=new t(e)
|
|
||||||
;e["on:end"](n,i),i.isMatchIgnored&&(s=!1)}if(s){
|
|
||||||
for(;e.endsParent&&e.parent;)e=e.parent;return e}}
|
|
||||||
if(e.endsWithParent)return f(e.parent,n,i)}function b(e){
|
|
||||||
return 0===N.matcher.regexIndex?(R+=e[0],1):(T=!0,0)}function m(e){
|
|
||||||
const t=e[0],i=n.substring(e.index),s=f(N,e,i);if(!s)return Q;const r=N
|
|
||||||
;N.endScope&&N.endScope._wrap?(g(),
|
|
||||||
u(t,N.endScope._wrap)):N.endScope&&N.endScope._multi?(g(),
|
|
||||||
d(N.endScope,e)):r.skip?R+=t:(r.returnEnd||r.excludeEnd||(R+=t),
|
|
||||||
g(),r.excludeEnd&&(R=t));do{
|
|
||||||
N.scope&&M.closeNode(),N.skip||N.subLanguage||(A+=N.relevance),N=N.parent
|
|
||||||
}while(N!==s.parent);return s.starts&&h(s.starts,e),r.returnEnd?0:t.length}
|
|
||||||
let _={};function y(i,r){const a=r&&r[0];if(R+=i,null==a)return g(),0
|
|
||||||
;if("begin"===_.type&&"end"===r.type&&_.index===r.index&&""===a){
|
|
||||||
if(R+=n.slice(r.index,r.index+1),!o){const t=Error(`0 width match regex (${e})`)
|
|
||||||
;throw t.languageName=e,t.badRule=_.rule,t}return 1}
|
|
||||||
if(_=r,"begin"===r.type)return(e=>{
|
|
||||||
const n=e[0],i=e.rule,s=new t(i),r=[i.__beforeBegin,i["on:begin"]]
|
|
||||||
;for(const t of r)if(t&&(t(e,s),s.isMatchIgnored))return b(n)
|
|
||||||
;return i.skip?R+=n:(i.excludeBegin&&(R+=n),
|
|
||||||
g(),i.returnBegin||i.excludeBegin||(R=n)),h(i,e),i.returnBegin?0:n.length})(r)
|
|
||||||
;if("illegal"===r.type&&!s){
|
|
||||||
const e=Error('Illegal lexeme "'+a+'" for mode "'+(N.scope||"<unnamed>")+'"')
|
|
||||||
;throw e.mode=N,e}if("end"===r.type){const e=m(r);if(e!==Q)return e}
|
|
||||||
if("illegal"===r.type&&""===a)return R+="\n",1
|
|
||||||
;if(I>1e5&&I>3*r.index)throw Error("potential infinite loop, way more iterations than matches")
|
|
||||||
;return R+=a,a.length}const w=O(e)
|
|
||||||
;if(!w)throw z(a.replace("{}",e)),Error('Unknown language: "'+e+'"')
|
|
||||||
;const k=Z(w);let v="",N=r||k;const S={},M=new p.__emitter(p);(()=>{const e=[]
|
|
||||||
;for(let t=N;t!==w;t=t.parent)t.scope&&e.unshift(t.scope)
|
|
||||||
;e.forEach((e=>M.openNode(e)))})();let R="",A=0,j=0,I=0,T=!1;try{
|
|
||||||
if(w.__emitTokens)w.__emitTokens(n,M);else{for(N.matcher.considerAll();;){
|
|
||||||
I++,T?T=!1:N.matcher.considerAll(),N.matcher.lastIndex=j
|
|
||||||
;const e=N.matcher.exec(n);if(!e)break;const t=y(n.substring(j,e.index),e)
|
|
||||||
;j=e.index+t}y(n.substring(j))}return M.finalize(),v=M.toHTML(),{language:e,
|
|
||||||
value:v,relevance:A,illegal:!1,_emitter:M,_top:N}}catch(t){
|
|
||||||
if(t.message&&t.message.includes("Illegal"))return{language:e,value:J(n),
|
|
||||||
illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j,
|
|
||||||
context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:v},_emitter:M};if(o)return{
|
|
||||||
language:e,value:J(n),illegal:!1,relevance:0,errorRaised:t,_emitter:M,_top:N}
|
|
||||||
;throw t}}function x(e,t){t=t||p.languages||Object.keys(i);const n=(e=>{
|
|
||||||
const t={value:J(e),illegal:!1,relevance:0,_top:l,_emitter:new p.__emitter(p)}
|
|
||||||
;return t._emitter.addText(e),t})(e),s=t.filter(O).filter(v).map((t=>E(t,e,!1)))
|
|
||||||
;s.unshift(n);const r=s.sort(((e,t)=>{
|
|
||||||
if(e.relevance!==t.relevance)return t.relevance-e.relevance
|
|
||||||
;if(e.language&&t.language){if(O(e.language).supersetOf===t.language)return 1
|
|
||||||
;if(O(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=r,c=o
|
|
||||||
;return c.secondBest=a,c}function _(e){let t=null;const n=(e=>{
|
|
||||||
let t=e.className+" ";t+=e.parentNode?e.parentNode.className:""
|
|
||||||
;const n=p.languageDetectRe.exec(t);if(n){const t=O(n[1])
|
|
||||||
;return t||(W(a.replace("{}",n[1])),
|
|
||||||
W("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}
|
|
||||||
return t.split(/\s+/).find((e=>b(e)||O(e)))})(e);if(b(n))return
|
|
||||||
;if(N("before:highlightElement",{el:e,language:n
|
|
||||||
}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e)
|
|
||||||
;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),
|
|
||||||
console.warn("https://github.com/highlightjs/highlight.js/wiki/security"),
|
|
||||||
console.warn("The element with unescaped HTML:"),
|
|
||||||
console.warn(e)),p.throwUnescapedHTML))throw new q("One of your code blocks includes unescaped HTML.",e.innerHTML)
|
|
||||||
;t=e;const i=t.textContent,r=n?m(i,{language:n,ignoreIllegals:!0}):x(i)
|
|
||||||
;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,t,n)=>{const i=t&&s[t]||n
|
|
||||||
;e.classList.add("hljs"),e.classList.add("language-"+i)
|
|
||||||
})(e,n,r.language),e.result={language:r.language,re:r.relevance,
|
|
||||||
relevance:r.relevance},r.secondBest&&(e.secondBest={
|
|
||||||
language:r.secondBest.language,relevance:r.secondBest.relevance
|
|
||||||
}),N("after:highlightElement",{el:e,result:r,text:i})}let y=!1;function w(){
|
|
||||||
if("loading"===document.readyState)return y||window.addEventListener("DOMContentLoaded",(()=>{
|
|
||||||
w()}),!1),void(y=!0);document.querySelectorAll(p.cssSelector).forEach(_)}
|
|
||||||
function O(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}
|
|
||||||
function k(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{
|
|
||||||
s[e.toLowerCase()]=t}))}function v(e){const t=O(e)
|
|
||||||
;return t&&!t.disableAutodetect}function N(e,t){const n=e;r.forEach((e=>{
|
|
||||||
e[n]&&e[n](t)}))}Object.assign(n,{highlight:m,highlightAuto:x,highlightAll:w,
|
|
||||||
highlightElement:_,
|
|
||||||
highlightBlock:e=>(X("10.7.0","highlightBlock will be removed entirely in v12.0"),
|
|
||||||
X("10.7.0","Please use highlightElement now."),_(e)),configure:e=>{p=Y(p,e)},
|
|
||||||
initHighlighting:()=>{
|
|
||||||
w(),X("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")},
|
|
||||||
initHighlightingOnLoad:()=>{
|
|
||||||
w(),X("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")
|
|
||||||
},registerLanguage:(e,t)=>{let s=null;try{s=t(n)}catch(t){
|
|
||||||
if(z("Language definition for '{}' could not be registered.".replace("{}",e)),
|
|
||||||
!o)throw t;z(t),s=l}
|
|
||||||
s.name||(s.name=e),i[e]=s,s.rawDefinition=t.bind(null,n),s.aliases&&k(s.aliases,{
|
|
||||||
languageName:e})},unregisterLanguage:e=>{delete i[e]
|
|
||||||
;for(const t of Object.keys(s))s[t]===e&&delete s[t]},
|
|
||||||
listLanguages:()=>Object.keys(i),getLanguage:O,registerAliases:k,
|
|
||||||
autoDetection:v,inherit:Y,addPlugin:e=>{(e=>{
|
|
||||||
e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{
|
|
||||||
e["before:highlightBlock"](Object.assign({block:t.el},t))
|
|
||||||
}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{
|
|
||||||
e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),r.push(e)},
|
|
||||||
removePlugin:e=>{const t=r.indexOf(e);-1!==t&&r.splice(t,1)}}),n.debugMode=()=>{
|
|
||||||
o=!1},n.safeMode=()=>{o=!0},n.versionString="11.11.1",n.regex={concat:h,
|
|
||||||
lookahead:g,either:f,optional:d,anyNumberOfTimes:u}
|
|
||||||
;for(const t in A)"object"==typeof A[t]&&e(A[t]);return Object.assign(n,A),n
|
|
||||||
},te=ee({});te.newInstance=()=>ee({});export{te as default};
|
|
||||||
@@ -1,417 +0,0 @@
|
|||||||
/*! `bash` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Bash
|
|
||||||
Author: vah <vahtenberg@gmail.com>
|
|
||||||
Contributrors: Benjamin Pannell <contact@sierrasoftworks.com>
|
|
||||||
Website: https://www.gnu.org/software/bash/
|
|
||||||
Category: common, scripting
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function bash(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const VAR = {};
|
|
||||||
const BRACED_VAR = {
|
|
||||||
begin: /\$\{/,
|
|
||||||
end: /\}/,
|
|
||||||
contains: [
|
|
||||||
"self",
|
|
||||||
{
|
|
||||||
begin: /:-/,
|
|
||||||
contains: [ VAR ]
|
|
||||||
} // default values
|
|
||||||
]
|
|
||||||
};
|
|
||||||
Object.assign(VAR, {
|
|
||||||
className: 'variable',
|
|
||||||
variants: [
|
|
||||||
{ begin: regex.concat(/\$[\w\d#@][\w\d_]*/,
|
|
||||||
// negative look-ahead tries to avoid matching patterns that are not
|
|
||||||
// Perl at all like $ident$, @ident@, etc.
|
|
||||||
`(?![\\w\\d])(?![$])`) },
|
|
||||||
BRACED_VAR
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const SUBST = {
|
|
||||||
className: 'subst',
|
|
||||||
begin: /\$\(/,
|
|
||||||
end: /\)/,
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
};
|
|
||||||
const COMMENT = hljs.inherit(
|
|
||||||
hljs.COMMENT(),
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/(^|\s)/,
|
|
||||||
/#.*$/
|
|
||||||
],
|
|
||||||
scope: {
|
|
||||||
2: 'comment'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const HERE_DOC = {
|
|
||||||
begin: /<<-?\s*(?=\w+)/,
|
|
||||||
starts: { contains: [
|
|
||||||
hljs.END_SAME_AS_BEGIN({
|
|
||||||
begin: /(\w+)/,
|
|
||||||
end: /(\w+)/,
|
|
||||||
className: 'string'
|
|
||||||
})
|
|
||||||
] }
|
|
||||||
};
|
|
||||||
const QUOTE_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /"/,
|
|
||||||
end: /"/,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
VAR,
|
|
||||||
SUBST
|
|
||||||
]
|
|
||||||
};
|
|
||||||
SUBST.contains.push(QUOTE_STRING);
|
|
||||||
const ESCAPED_QUOTE = {
|
|
||||||
match: /\\"/
|
|
||||||
};
|
|
||||||
const APOS_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /'/,
|
|
||||||
end: /'/
|
|
||||||
};
|
|
||||||
const ESCAPED_APOS = {
|
|
||||||
match: /\\'/
|
|
||||||
};
|
|
||||||
const ARITHMETIC = {
|
|
||||||
begin: /\$?\(\(/,
|
|
||||||
end: /\)\)/,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\d+#[0-9a-f]+/,
|
|
||||||
className: "number"
|
|
||||||
},
|
|
||||||
hljs.NUMBER_MODE,
|
|
||||||
VAR
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const SH_LIKE_SHELLS = [
|
|
||||||
"fish",
|
|
||||||
"bash",
|
|
||||||
"zsh",
|
|
||||||
"sh",
|
|
||||||
"csh",
|
|
||||||
"ksh",
|
|
||||||
"tcsh",
|
|
||||||
"dash",
|
|
||||||
"scsh",
|
|
||||||
];
|
|
||||||
const KNOWN_SHEBANG = hljs.SHEBANG({
|
|
||||||
binary: `(${SH_LIKE_SHELLS.join("|")})`,
|
|
||||||
relevance: 10
|
|
||||||
});
|
|
||||||
const FUNCTION = {
|
|
||||||
className: 'function',
|
|
||||||
begin: /\w[\w\d_]*\s*\(\s*\)\s*\{/,
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [ hljs.inherit(hljs.TITLE_MODE, { begin: /\w[\w\d_]*/ }) ],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const KEYWORDS = [
|
|
||||||
"if",
|
|
||||||
"then",
|
|
||||||
"else",
|
|
||||||
"elif",
|
|
||||||
"fi",
|
|
||||||
"time",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"until",
|
|
||||||
"in",
|
|
||||||
"do",
|
|
||||||
"done",
|
|
||||||
"case",
|
|
||||||
"esac",
|
|
||||||
"coproc",
|
|
||||||
"function",
|
|
||||||
"select"
|
|
||||||
];
|
|
||||||
|
|
||||||
const LITERALS = [
|
|
||||||
"true",
|
|
||||||
"false"
|
|
||||||
];
|
|
||||||
|
|
||||||
// to consume paths to prevent keyword matches inside them
|
|
||||||
const PATH_MODE = { match: /(\/[a-z._-]+)+/ };
|
|
||||||
|
|
||||||
// http://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
|
|
||||||
const SHELL_BUILT_INS = [
|
|
||||||
"break",
|
|
||||||
"cd",
|
|
||||||
"continue",
|
|
||||||
"eval",
|
|
||||||
"exec",
|
|
||||||
"exit",
|
|
||||||
"export",
|
|
||||||
"getopts",
|
|
||||||
"hash",
|
|
||||||
"pwd",
|
|
||||||
"readonly",
|
|
||||||
"return",
|
|
||||||
"shift",
|
|
||||||
"test",
|
|
||||||
"times",
|
|
||||||
"trap",
|
|
||||||
"umask",
|
|
||||||
"unset"
|
|
||||||
];
|
|
||||||
|
|
||||||
const BASH_BUILT_INS = [
|
|
||||||
"alias",
|
|
||||||
"bind",
|
|
||||||
"builtin",
|
|
||||||
"caller",
|
|
||||||
"command",
|
|
||||||
"declare",
|
|
||||||
"echo",
|
|
||||||
"enable",
|
|
||||||
"help",
|
|
||||||
"let",
|
|
||||||
"local",
|
|
||||||
"logout",
|
|
||||||
"mapfile",
|
|
||||||
"printf",
|
|
||||||
"read",
|
|
||||||
"readarray",
|
|
||||||
"source",
|
|
||||||
"sudo",
|
|
||||||
"type",
|
|
||||||
"typeset",
|
|
||||||
"ulimit",
|
|
||||||
"unalias"
|
|
||||||
];
|
|
||||||
|
|
||||||
const ZSH_BUILT_INS = [
|
|
||||||
"autoload",
|
|
||||||
"bg",
|
|
||||||
"bindkey",
|
|
||||||
"bye",
|
|
||||||
"cap",
|
|
||||||
"chdir",
|
|
||||||
"clone",
|
|
||||||
"comparguments",
|
|
||||||
"compcall",
|
|
||||||
"compctl",
|
|
||||||
"compdescribe",
|
|
||||||
"compfiles",
|
|
||||||
"compgroups",
|
|
||||||
"compquote",
|
|
||||||
"comptags",
|
|
||||||
"comptry",
|
|
||||||
"compvalues",
|
|
||||||
"dirs",
|
|
||||||
"disable",
|
|
||||||
"disown",
|
|
||||||
"echotc",
|
|
||||||
"echoti",
|
|
||||||
"emulate",
|
|
||||||
"fc",
|
|
||||||
"fg",
|
|
||||||
"float",
|
|
||||||
"functions",
|
|
||||||
"getcap",
|
|
||||||
"getln",
|
|
||||||
"history",
|
|
||||||
"integer",
|
|
||||||
"jobs",
|
|
||||||
"kill",
|
|
||||||
"limit",
|
|
||||||
"log",
|
|
||||||
"noglob",
|
|
||||||
"popd",
|
|
||||||
"print",
|
|
||||||
"pushd",
|
|
||||||
"pushln",
|
|
||||||
"rehash",
|
|
||||||
"sched",
|
|
||||||
"setcap",
|
|
||||||
"setopt",
|
|
||||||
"stat",
|
|
||||||
"suspend",
|
|
||||||
"ttyctl",
|
|
||||||
"unfunction",
|
|
||||||
"unhash",
|
|
||||||
"unlimit",
|
|
||||||
"unsetopt",
|
|
||||||
"vared",
|
|
||||||
"wait",
|
|
||||||
"whence",
|
|
||||||
"where",
|
|
||||||
"which",
|
|
||||||
"zcompile",
|
|
||||||
"zformat",
|
|
||||||
"zftp",
|
|
||||||
"zle",
|
|
||||||
"zmodload",
|
|
||||||
"zparseopts",
|
|
||||||
"zprof",
|
|
||||||
"zpty",
|
|
||||||
"zregexparse",
|
|
||||||
"zsocket",
|
|
||||||
"zstyle",
|
|
||||||
"ztcp"
|
|
||||||
];
|
|
||||||
|
|
||||||
const GNU_CORE_UTILS = [
|
|
||||||
"chcon",
|
|
||||||
"chgrp",
|
|
||||||
"chown",
|
|
||||||
"chmod",
|
|
||||||
"cp",
|
|
||||||
"dd",
|
|
||||||
"df",
|
|
||||||
"dir",
|
|
||||||
"dircolors",
|
|
||||||
"ln",
|
|
||||||
"ls",
|
|
||||||
"mkdir",
|
|
||||||
"mkfifo",
|
|
||||||
"mknod",
|
|
||||||
"mktemp",
|
|
||||||
"mv",
|
|
||||||
"realpath",
|
|
||||||
"rm",
|
|
||||||
"rmdir",
|
|
||||||
"shred",
|
|
||||||
"sync",
|
|
||||||
"touch",
|
|
||||||
"truncate",
|
|
||||||
"vdir",
|
|
||||||
"b2sum",
|
|
||||||
"base32",
|
|
||||||
"base64",
|
|
||||||
"cat",
|
|
||||||
"cksum",
|
|
||||||
"comm",
|
|
||||||
"csplit",
|
|
||||||
"cut",
|
|
||||||
"expand",
|
|
||||||
"fmt",
|
|
||||||
"fold",
|
|
||||||
"head",
|
|
||||||
"join",
|
|
||||||
"md5sum",
|
|
||||||
"nl",
|
|
||||||
"numfmt",
|
|
||||||
"od",
|
|
||||||
"paste",
|
|
||||||
"ptx",
|
|
||||||
"pr",
|
|
||||||
"sha1sum",
|
|
||||||
"sha224sum",
|
|
||||||
"sha256sum",
|
|
||||||
"sha384sum",
|
|
||||||
"sha512sum",
|
|
||||||
"shuf",
|
|
||||||
"sort",
|
|
||||||
"split",
|
|
||||||
"sum",
|
|
||||||
"tac",
|
|
||||||
"tail",
|
|
||||||
"tr",
|
|
||||||
"tsort",
|
|
||||||
"unexpand",
|
|
||||||
"uniq",
|
|
||||||
"wc",
|
|
||||||
"arch",
|
|
||||||
"basename",
|
|
||||||
"chroot",
|
|
||||||
"date",
|
|
||||||
"dirname",
|
|
||||||
"du",
|
|
||||||
"echo",
|
|
||||||
"env",
|
|
||||||
"expr",
|
|
||||||
"factor",
|
|
||||||
// "false", // keyword literal already
|
|
||||||
"groups",
|
|
||||||
"hostid",
|
|
||||||
"id",
|
|
||||||
"link",
|
|
||||||
"logname",
|
|
||||||
"nice",
|
|
||||||
"nohup",
|
|
||||||
"nproc",
|
|
||||||
"pathchk",
|
|
||||||
"pinky",
|
|
||||||
"printenv",
|
|
||||||
"printf",
|
|
||||||
"pwd",
|
|
||||||
"readlink",
|
|
||||||
"runcon",
|
|
||||||
"seq",
|
|
||||||
"sleep",
|
|
||||||
"stat",
|
|
||||||
"stdbuf",
|
|
||||||
"stty",
|
|
||||||
"tee",
|
|
||||||
"test",
|
|
||||||
"timeout",
|
|
||||||
// "true", // keyword literal already
|
|
||||||
"tty",
|
|
||||||
"uname",
|
|
||||||
"unlink",
|
|
||||||
"uptime",
|
|
||||||
"users",
|
|
||||||
"who",
|
|
||||||
"whoami",
|
|
||||||
"yes"
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Bash',
|
|
||||||
aliases: [
|
|
||||||
'sh',
|
|
||||||
'zsh'
|
|
||||||
],
|
|
||||||
keywords: {
|
|
||||||
$pattern: /\b[a-z][a-z0-9._-]+\b/,
|
|
||||||
keyword: KEYWORDS,
|
|
||||||
literal: LITERALS,
|
|
||||||
built_in: [
|
|
||||||
...SHELL_BUILT_INS,
|
|
||||||
...BASH_BUILT_INS,
|
|
||||||
// Shell modifiers
|
|
||||||
"set",
|
|
||||||
"shopt",
|
|
||||||
...ZSH_BUILT_INS,
|
|
||||||
...GNU_CORE_UTILS
|
|
||||||
]
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
KNOWN_SHEBANG, // to catch known shells and boost relevancy
|
|
||||||
hljs.SHEBANG(), // to catch unknown shells but still highlight the shebang
|
|
||||||
FUNCTION,
|
|
||||||
ARITHMETIC,
|
|
||||||
COMMENT,
|
|
||||||
HERE_DOC,
|
|
||||||
PATH_MODE,
|
|
||||||
QUOTE_STRING,
|
|
||||||
ESCAPED_QUOTE,
|
|
||||||
APOS_STRING,
|
|
||||||
ESCAPED_APOS,
|
|
||||||
VAR
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return bash;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/*! `bash` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const s=e.regex,t={},a={
|
|
||||||
begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]}
|
|
||||||
;Object.assign(t,{className:"variable",variants:[{
|
|
||||||
begin:s.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const n={
|
|
||||||
className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]
|
|
||||||
},c=e.inherit(e.COMMENT(),{match:[/(^|\s)/,/#.*$/],scope:{2:"comment"}}),i={
|
|
||||||
begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,
|
|
||||||
end:/(\w+)/,className:"string"})]}},o={className:"string",begin:/"/,end:/"/,
|
|
||||||
contains:[e.BACKSLASH_ESCAPE,t,n]};n.contains.push(o);const r={begin:/\$?\(\(/,
|
|
||||||
end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t]
|
|
||||||
},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10
|
|
||||||
}),m={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,
|
|
||||||
contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{
|
|
||||||
name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/,
|
|
||||||
keyword:["if","then","else","elif","fi","time","for","while","until","in","do","done","case","esac","coproc","function","select"],
|
|
||||||
literal:["true","false"],
|
|
||||||
built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","sudo","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"]
|
|
||||||
},contains:[l,e.SHEBANG(),m,r,c,i,{match:/(\/[a-z._-]+)+/},o,{match:/\\"/},{
|
|
||||||
className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
/*! `c` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: C
|
|
||||||
Category: common, system
|
|
||||||
Website: https://en.wikipedia.org/wiki/C_(programming_language)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function c(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
// added for historic reasons because `hljs.C_LINE_COMMENT_MODE` does
|
|
||||||
// not include such support nor can we be sure all the grammars depending
|
|
||||||
// on it would desire this behavior
|
|
||||||
const C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$', { contains: [ { begin: /\\\n/ } ] });
|
|
||||||
const DECLTYPE_AUTO_RE = 'decltype\\(auto\\)';
|
|
||||||
const NAMESPACE_RE = '[a-zA-Z_]\\w*::';
|
|
||||||
const TEMPLATE_ARGUMENT_RE = '<[^<>]+>';
|
|
||||||
const FUNCTION_TYPE_RE = '('
|
|
||||||
+ DECLTYPE_AUTO_RE + '|'
|
|
||||||
+ regex.optional(NAMESPACE_RE)
|
|
||||||
+ '[a-zA-Z_]\\w*' + regex.optional(TEMPLATE_ARGUMENT_RE)
|
|
||||||
+ ')';
|
|
||||||
|
|
||||||
|
|
||||||
const TYPES = {
|
|
||||||
className: 'type',
|
|
||||||
variants: [
|
|
||||||
{ begin: '\\b[a-z\\d_]*_t\\b' },
|
|
||||||
{ match: /\batomic_[a-z]{3,6}\b/ }
|
|
||||||
]
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/language/escape
|
|
||||||
// \\ \x \xFF \u2837 \u00323747 \374
|
|
||||||
const CHARACTER_ESCAPES = '\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)';
|
|
||||||
const STRINGS = {
|
|
||||||
className: 'string',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '(u8?|U|L)?"',
|
|
||||||
end: '"',
|
|
||||||
illegal: '\\n',
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '(u8?|U|L)?\'(' + CHARACTER_ESCAPES + "|.)",
|
|
||||||
end: '\'',
|
|
||||||
illegal: '.'
|
|
||||||
},
|
|
||||||
hljs.END_SAME_AS_BEGIN({
|
|
||||||
begin: /(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,
|
|
||||||
end: /\)([^()\\ ]{0,16})"/
|
|
||||||
})
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const NUMBERS = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
{ match: /\b(0b[01']+)/ },
|
|
||||||
{ match: /(-?)\b([\d']+(\.[\d']*)?|\.[\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/ },
|
|
||||||
{ match: /(-?)\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/ },
|
|
||||||
{ match: /(-?)\b\d+(?:'\d+)*(?:\.\d*(?:'\d*)*)?(?:[eE][-+]?\d+)?/ }
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const PREPROCESSOR = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: /#\s*[a-z]+\b/,
|
|
||||||
end: /$/,
|
|
||||||
keywords: { keyword:
|
|
||||||
'if else elif endif define undef warning error line '
|
|
||||||
+ 'pragma _Pragma ifdef ifndef elifdef elifndef include' },
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\\\n/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
hljs.inherit(STRINGS, { className: 'string' }),
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
begin: /<.*?>/
|
|
||||||
},
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const TITLE_MODE = {
|
|
||||||
className: 'title',
|
|
||||||
begin: regex.optional(NAMESPACE_RE) + hljs.IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_TITLE = regex.optional(NAMESPACE_RE) + hljs.IDENT_RE + '\\s*\\(';
|
|
||||||
|
|
||||||
const C_KEYWORDS = [
|
|
||||||
"asm",
|
|
||||||
"auto",
|
|
||||||
"break",
|
|
||||||
"case",
|
|
||||||
"continue",
|
|
||||||
"default",
|
|
||||||
"do",
|
|
||||||
"else",
|
|
||||||
"enum",
|
|
||||||
"extern",
|
|
||||||
"for",
|
|
||||||
"fortran",
|
|
||||||
"goto",
|
|
||||||
"if",
|
|
||||||
"inline",
|
|
||||||
"register",
|
|
||||||
"restrict",
|
|
||||||
"return",
|
|
||||||
"sizeof",
|
|
||||||
"typeof",
|
|
||||||
"typeof_unqual",
|
|
||||||
"struct",
|
|
||||||
"switch",
|
|
||||||
"typedef",
|
|
||||||
"union",
|
|
||||||
"volatile",
|
|
||||||
"while",
|
|
||||||
"_Alignas",
|
|
||||||
"_Alignof",
|
|
||||||
"_Atomic",
|
|
||||||
"_Generic",
|
|
||||||
"_Noreturn",
|
|
||||||
"_Static_assert",
|
|
||||||
"_Thread_local",
|
|
||||||
// aliases
|
|
||||||
"alignas",
|
|
||||||
"alignof",
|
|
||||||
"noreturn",
|
|
||||||
"static_assert",
|
|
||||||
"thread_local",
|
|
||||||
// not a C keyword but is, for all intents and purposes, treated exactly like one.
|
|
||||||
"_Pragma"
|
|
||||||
];
|
|
||||||
|
|
||||||
const C_TYPES = [
|
|
||||||
"float",
|
|
||||||
"double",
|
|
||||||
"signed",
|
|
||||||
"unsigned",
|
|
||||||
"int",
|
|
||||||
"short",
|
|
||||||
"long",
|
|
||||||
"char",
|
|
||||||
"void",
|
|
||||||
"_Bool",
|
|
||||||
"_BitInt",
|
|
||||||
"_Complex",
|
|
||||||
"_Imaginary",
|
|
||||||
"_Decimal32",
|
|
||||||
"_Decimal64",
|
|
||||||
"_Decimal96",
|
|
||||||
"_Decimal128",
|
|
||||||
"_Decimal64x",
|
|
||||||
"_Decimal128x",
|
|
||||||
"_Float16",
|
|
||||||
"_Float32",
|
|
||||||
"_Float64",
|
|
||||||
"_Float128",
|
|
||||||
"_Float32x",
|
|
||||||
"_Float64x",
|
|
||||||
"_Float128x",
|
|
||||||
// modifiers
|
|
||||||
"const",
|
|
||||||
"static",
|
|
||||||
"constexpr",
|
|
||||||
// aliases
|
|
||||||
"complex",
|
|
||||||
"bool",
|
|
||||||
"imaginary"
|
|
||||||
];
|
|
||||||
|
|
||||||
const KEYWORDS = {
|
|
||||||
keyword: C_KEYWORDS,
|
|
||||||
type: C_TYPES,
|
|
||||||
literal: 'true false NULL',
|
|
||||||
// TODO: apply hinting work similar to what was done in cpp.js
|
|
||||||
built_in: 'std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream '
|
|
||||||
+ 'auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set '
|
|
||||||
+ 'unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos '
|
|
||||||
+ 'asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp '
|
|
||||||
+ 'fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper '
|
|
||||||
+ 'isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow '
|
|
||||||
+ 'printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp '
|
|
||||||
+ 'strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan '
|
|
||||||
+ 'vfprintf vprintf vsprintf endl initializer_list unique_ptr',
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXPRESSION_CONTAINS = [
|
|
||||||
PREPROCESSOR,
|
|
||||||
TYPES,
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
NUMBERS,
|
|
||||||
STRINGS
|
|
||||||
];
|
|
||||||
|
|
||||||
const EXPRESSION_CONTEXT = {
|
|
||||||
// This mode covers expression context where we can't expect a function
|
|
||||||
// definition and shouldn't highlight anything that looks like one:
|
|
||||||
// `return some()`, `else if()`, `(x*sum(1, 2))`
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: /=/,
|
|
||||||
end: /;/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginKeywords: 'new throw return else',
|
|
||||||
end: /;/
|
|
||||||
}
|
|
||||||
],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
contains: EXPRESSION_CONTAINS.concat([
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
contains: EXPRESSION_CONTAINS.concat([ 'self' ]),
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_DECLARATION = {
|
|
||||||
begin: '(' + FUNCTION_TYPE_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
|
|
||||||
returnBegin: true,
|
|
||||||
end: /[{;=]/,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: /[^\w\s\*&:<>.]/,
|
|
||||||
contains: [
|
|
||||||
{ // to prevent it from being confused as the function title
|
|
||||||
begin: DECLTYPE_AUTO_RE,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: FUNCTION_TITLE,
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [ hljs.inherit(TITLE_MODE, { className: "title.function" }) ],
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
// allow for multiple declarations, e.g.:
|
|
||||||
// extern void f(int), g(char);
|
|
||||||
{
|
|
||||||
relevance: 0,
|
|
||||||
match: /,/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS,
|
|
||||||
TYPES,
|
|
||||||
// Count matching parentheses.
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
'self',
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS,
|
|
||||||
TYPES
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
TYPES,
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
PREPROCESSOR
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: "C",
|
|
||||||
aliases: [ 'h' ],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
// Until differentiations are added between `c` and `cpp`, `c` will
|
|
||||||
// not be auto-detected to avoid auto-detect conflicts between C and C++
|
|
||||||
disableAutodetect: true,
|
|
||||||
illegal: '</',
|
|
||||||
contains: [].concat(
|
|
||||||
EXPRESSION_CONTEXT,
|
|
||||||
FUNCTION_DECLARATION,
|
|
||||||
EXPRESSION_CONTAINS,
|
|
||||||
[
|
|
||||||
PREPROCESSOR,
|
|
||||||
{
|
|
||||||
begin: hljs.IDENT_RE + '::',
|
|
||||||
keywords: KEYWORDS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'class',
|
|
||||||
beginKeywords: 'enum class struct union',
|
|
||||||
end: /[{;:<>=]/,
|
|
||||||
contains: [
|
|
||||||
{ beginKeywords: "final class struct" },
|
|
||||||
hljs.TITLE_MODE
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
exports: {
|
|
||||||
preprocessor: PREPROCESSOR,
|
|
||||||
strings: STRINGS,
|
|
||||||
keywords: KEYWORDS
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return c;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/*! `c` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{
|
|
||||||
const t=e.regex,a=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]
|
|
||||||
}),n="decltype\\(auto\\)",s="[a-zA-Z_]\\w*::",r="("+n+"|"+t.optional(s)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",i={
|
|
||||||
className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{
|
|
||||||
match:/\batomic_[a-z]{3,6}\b/}]},l={className:"string",variants:[{
|
|
||||||
begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{
|
|
||||||
begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
|
|
||||||
end:"'",illegal:"."},e.END_SAME_AS_BEGIN({
|
|
||||||
begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
|
|
||||||
className:"number",variants:[{match:/\b(0b[01']+)/},{
|
|
||||||
match:/(-?)\b([\d']+(\.[\d']*)?|\.[\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)/
|
|
||||||
},{
|
|
||||||
match:/(-?)\b(0[xX][a-fA-F0-9]+(?:'[a-fA-F0-9]+)*(?:\.[a-fA-F0-9]*(?:'[a-fA-F0-9]*)*)?(?:[pP][-+]?[0-9]+)?(l|L)?(u|U)?)/
|
|
||||||
},{match:/(-?)\b\d+(?:'\d+)*(?:\.\d*(?:'\d*)*)?(?:[eE][-+]?\d+)?/}],relevance:0
|
|
||||||
},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
|
|
||||||
keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef elifdef elifndef include"
|
|
||||||
},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{
|
|
||||||
className:"string",begin:/<.*?>/},a,e.C_BLOCK_COMMENT_MODE]},d={
|
|
||||||
className:"title",begin:t.optional(s)+e.IDENT_RE,relevance:0
|
|
||||||
},m=t.optional(s)+e.IDENT_RE+"\\s*\\(",_={
|
|
||||||
keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","typeof","typeof_unqual","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],
|
|
||||||
type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_BitInt","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal96","_Decimal128","_Decimal64x","_Decimal128x","_Float16","_Float32","_Float64","_Float128","_Float32x","_Float64x","_Float128x","const","static","constexpr","complex","bool","imaginary"],
|
|
||||||
literal:"true false NULL",
|
|
||||||
built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"
|
|
||||||
},u=[c,i,a,e.C_BLOCK_COMMENT_MODE,o,l],p={variants:[{begin:/=/,end:/;/},{
|
|
||||||
begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],
|
|
||||||
keywords:_,contains:u.concat([{begin:/\(/,end:/\)/,keywords:_,
|
|
||||||
contains:u.concat(["self"]),relevance:0}]),relevance:0},f={
|
|
||||||
begin:"("+r+"[\\*&\\s]+)+"+m,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,
|
|
||||||
keywords:_,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:n,keywords:_,relevance:0},{
|
|
||||||
begin:m,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})],
|
|
||||||
relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,
|
|
||||||
keywords:_,relevance:0,contains:[a,e.C_BLOCK_COMMENT_MODE,l,o,i,{begin:/\(/,
|
|
||||||
end:/\)/,keywords:_,relevance:0,contains:["self",a,e.C_BLOCK_COMMENT_MODE,l,o,i]
|
|
||||||
}]},i,a,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:_,
|
|
||||||
disableAutodetect:!0,illegal:"</",contains:[].concat(p,f,u,[c,{
|
|
||||||
begin:e.IDENT_RE+"::",keywords:_},{className:"class",
|
|
||||||
beginKeywords:"enum class struct union",end:/[{;:<>=]/,contains:[{
|
|
||||||
beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c,
|
|
||||||
strings:l,keywords:_}}}})();export default hljsGrammar;
|
|
||||||
@@ -1,613 +0,0 @@
|
|||||||
/*! `cpp` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: C++
|
|
||||||
Category: common, system
|
|
||||||
Website: https://isocpp.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function cpp(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
// added for historic reasons because `hljs.C_LINE_COMMENT_MODE` does
|
|
||||||
// not include such support nor can we be sure all the grammars depending
|
|
||||||
// on it would desire this behavior
|
|
||||||
const C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$', { contains: [ { begin: /\\\n/ } ] });
|
|
||||||
const DECLTYPE_AUTO_RE = 'decltype\\(auto\\)';
|
|
||||||
const NAMESPACE_RE = '[a-zA-Z_]\\w*::';
|
|
||||||
const TEMPLATE_ARGUMENT_RE = '<[^<>]+>';
|
|
||||||
const FUNCTION_TYPE_RE = '(?!struct)('
|
|
||||||
+ DECLTYPE_AUTO_RE + '|'
|
|
||||||
+ regex.optional(NAMESPACE_RE)
|
|
||||||
+ '[a-zA-Z_]\\w*' + regex.optional(TEMPLATE_ARGUMENT_RE)
|
|
||||||
+ ')';
|
|
||||||
|
|
||||||
const CPP_PRIMITIVE_TYPES = {
|
|
||||||
className: 'type',
|
|
||||||
begin: '\\b[a-z\\d_]*_t\\b'
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/language/escape
|
|
||||||
// \\ \x \xFF \u2837 \u00323747 \374
|
|
||||||
const CHARACTER_ESCAPES = '\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)';
|
|
||||||
const STRINGS = {
|
|
||||||
className: 'string',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '(u8?|U|L)?"',
|
|
||||||
end: '"',
|
|
||||||
illegal: '\\n',
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '(u8?|U|L)?\'(' + CHARACTER_ESCAPES + '|.)',
|
|
||||||
end: '\'',
|
|
||||||
illegal: '.'
|
|
||||||
},
|
|
||||||
hljs.END_SAME_AS_BEGIN({
|
|
||||||
begin: /(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,
|
|
||||||
end: /\)([^()\\ ]{0,16})"/
|
|
||||||
})
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const NUMBERS = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
// Floating-point literal.
|
|
||||||
{ begin:
|
|
||||||
"[+-]?(?:" // Leading sign.
|
|
||||||
// Decimal.
|
|
||||||
+ "(?:"
|
|
||||||
+"[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?"
|
|
||||||
+ "|\\.[0-9](?:'?[0-9])*"
|
|
||||||
+ ")(?:[Ee][+-]?[0-9](?:'?[0-9])*)?"
|
|
||||||
+ "|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*"
|
|
||||||
// Hexadecimal.
|
|
||||||
+ "|0[Xx](?:"
|
|
||||||
+"[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?"
|
|
||||||
+ "|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*"
|
|
||||||
+ ")[Pp][+-]?[0-9](?:'?[0-9])*"
|
|
||||||
+ ")(?:" // Literal suffixes.
|
|
||||||
+ "[Ff](?:16|32|64|128)?"
|
|
||||||
+ "|(BF|bf)16"
|
|
||||||
+ "|[Ll]"
|
|
||||||
+ "|" // Literal suffix is optional.
|
|
||||||
+ ")"
|
|
||||||
},
|
|
||||||
// Integer literal.
|
|
||||||
{ begin:
|
|
||||||
"[+-]?\\b(?:" // Leading sign.
|
|
||||||
+ "0[Bb][01](?:'?[01])*" // Binary.
|
|
||||||
+ "|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*" // Hexadecimal.
|
|
||||||
+ "|0(?:'?[0-7])*" // Octal or just a lone zero.
|
|
||||||
+ "|[1-9](?:'?[0-9])*" // Decimal.
|
|
||||||
+ ")(?:" // Literal suffixes.
|
|
||||||
+ "[Uu](?:LL?|ll?)"
|
|
||||||
+ "|[Uu][Zz]?"
|
|
||||||
+ "|(?:LL?|ll?)[Uu]?"
|
|
||||||
+ "|[Zz][Uu]"
|
|
||||||
+ "|" // Literal suffix is optional.
|
|
||||||
+ ")"
|
|
||||||
// Note: there are user-defined literal suffixes too, but perhaps having the custom suffix not part of the
|
|
||||||
// literal highlight actually makes it stand out more.
|
|
||||||
}
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const PREPROCESSOR = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: /#\s*[a-z]+\b/,
|
|
||||||
end: /$/,
|
|
||||||
keywords: { keyword:
|
|
||||||
'if else elif endif define undef warning error line '
|
|
||||||
+ 'pragma _Pragma ifdef ifndef include' },
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\\\n/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
hljs.inherit(STRINGS, { className: 'string' }),
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
begin: /<.*?>/
|
|
||||||
},
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const TITLE_MODE = {
|
|
||||||
className: 'title',
|
|
||||||
begin: regex.optional(NAMESPACE_RE) + hljs.IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_TITLE = regex.optional(NAMESPACE_RE) + hljs.IDENT_RE + '\\s*\\(';
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/keyword
|
|
||||||
const RESERVED_KEYWORDS = [
|
|
||||||
'alignas',
|
|
||||||
'alignof',
|
|
||||||
'and',
|
|
||||||
'and_eq',
|
|
||||||
'asm',
|
|
||||||
'atomic_cancel',
|
|
||||||
'atomic_commit',
|
|
||||||
'atomic_noexcept',
|
|
||||||
'auto',
|
|
||||||
'bitand',
|
|
||||||
'bitor',
|
|
||||||
'break',
|
|
||||||
'case',
|
|
||||||
'catch',
|
|
||||||
'class',
|
|
||||||
'co_await',
|
|
||||||
'co_return',
|
|
||||||
'co_yield',
|
|
||||||
'compl',
|
|
||||||
'concept',
|
|
||||||
'const_cast|10',
|
|
||||||
'consteval',
|
|
||||||
'constexpr',
|
|
||||||
'constinit',
|
|
||||||
'continue',
|
|
||||||
'decltype',
|
|
||||||
'default',
|
|
||||||
'delete',
|
|
||||||
'do',
|
|
||||||
'dynamic_cast|10',
|
|
||||||
'else',
|
|
||||||
'enum',
|
|
||||||
'explicit',
|
|
||||||
'export',
|
|
||||||
'extern',
|
|
||||||
'false',
|
|
||||||
'final',
|
|
||||||
'for',
|
|
||||||
'friend',
|
|
||||||
'goto',
|
|
||||||
'if',
|
|
||||||
'import',
|
|
||||||
'inline',
|
|
||||||
'module',
|
|
||||||
'mutable',
|
|
||||||
'namespace',
|
|
||||||
'new',
|
|
||||||
'noexcept',
|
|
||||||
'not',
|
|
||||||
'not_eq',
|
|
||||||
'nullptr',
|
|
||||||
'operator',
|
|
||||||
'or',
|
|
||||||
'or_eq',
|
|
||||||
'override',
|
|
||||||
'private',
|
|
||||||
'protected',
|
|
||||||
'public',
|
|
||||||
'reflexpr',
|
|
||||||
'register',
|
|
||||||
'reinterpret_cast|10',
|
|
||||||
'requires',
|
|
||||||
'return',
|
|
||||||
'sizeof',
|
|
||||||
'static_assert',
|
|
||||||
'static_cast|10',
|
|
||||||
'struct',
|
|
||||||
'switch',
|
|
||||||
'synchronized',
|
|
||||||
'template',
|
|
||||||
'this',
|
|
||||||
'thread_local',
|
|
||||||
'throw',
|
|
||||||
'transaction_safe',
|
|
||||||
'transaction_safe_dynamic',
|
|
||||||
'true',
|
|
||||||
'try',
|
|
||||||
'typedef',
|
|
||||||
'typeid',
|
|
||||||
'typename',
|
|
||||||
'union',
|
|
||||||
'using',
|
|
||||||
'virtual',
|
|
||||||
'volatile',
|
|
||||||
'while',
|
|
||||||
'xor',
|
|
||||||
'xor_eq'
|
|
||||||
];
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/keyword
|
|
||||||
const RESERVED_TYPES = [
|
|
||||||
'bool',
|
|
||||||
'char',
|
|
||||||
'char16_t',
|
|
||||||
'char32_t',
|
|
||||||
'char8_t',
|
|
||||||
'double',
|
|
||||||
'float',
|
|
||||||
'int',
|
|
||||||
'long',
|
|
||||||
'short',
|
|
||||||
'void',
|
|
||||||
'wchar_t',
|
|
||||||
'unsigned',
|
|
||||||
'signed',
|
|
||||||
'const',
|
|
||||||
'static'
|
|
||||||
];
|
|
||||||
|
|
||||||
const TYPE_HINTS = [
|
|
||||||
'any',
|
|
||||||
'auto_ptr',
|
|
||||||
'barrier',
|
|
||||||
'binary_semaphore',
|
|
||||||
'bitset',
|
|
||||||
'complex',
|
|
||||||
'condition_variable',
|
|
||||||
'condition_variable_any',
|
|
||||||
'counting_semaphore',
|
|
||||||
'deque',
|
|
||||||
'false_type',
|
|
||||||
'flat_map',
|
|
||||||
'flat_set',
|
|
||||||
'future',
|
|
||||||
'imaginary',
|
|
||||||
'initializer_list',
|
|
||||||
'istringstream',
|
|
||||||
'jthread',
|
|
||||||
'latch',
|
|
||||||
'lock_guard',
|
|
||||||
'multimap',
|
|
||||||
'multiset',
|
|
||||||
'mutex',
|
|
||||||
'optional',
|
|
||||||
'ostringstream',
|
|
||||||
'packaged_task',
|
|
||||||
'pair',
|
|
||||||
'promise',
|
|
||||||
'priority_queue',
|
|
||||||
'queue',
|
|
||||||
'recursive_mutex',
|
|
||||||
'recursive_timed_mutex',
|
|
||||||
'scoped_lock',
|
|
||||||
'set',
|
|
||||||
'shared_future',
|
|
||||||
'shared_lock',
|
|
||||||
'shared_mutex',
|
|
||||||
'shared_timed_mutex',
|
|
||||||
'shared_ptr',
|
|
||||||
'stack',
|
|
||||||
'string_view',
|
|
||||||
'stringstream',
|
|
||||||
'timed_mutex',
|
|
||||||
'thread',
|
|
||||||
'true_type',
|
|
||||||
'tuple',
|
|
||||||
'unique_lock',
|
|
||||||
'unique_ptr',
|
|
||||||
'unordered_map',
|
|
||||||
'unordered_multimap',
|
|
||||||
'unordered_multiset',
|
|
||||||
'unordered_set',
|
|
||||||
'variant',
|
|
||||||
'vector',
|
|
||||||
'weak_ptr',
|
|
||||||
'wstring',
|
|
||||||
'wstring_view'
|
|
||||||
];
|
|
||||||
|
|
||||||
const FUNCTION_HINTS = [
|
|
||||||
'abort',
|
|
||||||
'abs',
|
|
||||||
'acos',
|
|
||||||
'apply',
|
|
||||||
'as_const',
|
|
||||||
'asin',
|
|
||||||
'atan',
|
|
||||||
'atan2',
|
|
||||||
'calloc',
|
|
||||||
'ceil',
|
|
||||||
'cerr',
|
|
||||||
'cin',
|
|
||||||
'clog',
|
|
||||||
'cos',
|
|
||||||
'cosh',
|
|
||||||
'cout',
|
|
||||||
'declval',
|
|
||||||
'endl',
|
|
||||||
'exchange',
|
|
||||||
'exit',
|
|
||||||
'exp',
|
|
||||||
'fabs',
|
|
||||||
'floor',
|
|
||||||
'fmod',
|
|
||||||
'forward',
|
|
||||||
'fprintf',
|
|
||||||
'fputs',
|
|
||||||
'free',
|
|
||||||
'frexp',
|
|
||||||
'fscanf',
|
|
||||||
'future',
|
|
||||||
'invoke',
|
|
||||||
'isalnum',
|
|
||||||
'isalpha',
|
|
||||||
'iscntrl',
|
|
||||||
'isdigit',
|
|
||||||
'isgraph',
|
|
||||||
'islower',
|
|
||||||
'isprint',
|
|
||||||
'ispunct',
|
|
||||||
'isspace',
|
|
||||||
'isupper',
|
|
||||||
'isxdigit',
|
|
||||||
'labs',
|
|
||||||
'launder',
|
|
||||||
'ldexp',
|
|
||||||
'log',
|
|
||||||
'log10',
|
|
||||||
'make_pair',
|
|
||||||
'make_shared',
|
|
||||||
'make_shared_for_overwrite',
|
|
||||||
'make_tuple',
|
|
||||||
'make_unique',
|
|
||||||
'malloc',
|
|
||||||
'memchr',
|
|
||||||
'memcmp',
|
|
||||||
'memcpy',
|
|
||||||
'memset',
|
|
||||||
'modf',
|
|
||||||
'move',
|
|
||||||
'pow',
|
|
||||||
'printf',
|
|
||||||
'putchar',
|
|
||||||
'puts',
|
|
||||||
'realloc',
|
|
||||||
'scanf',
|
|
||||||
'sin',
|
|
||||||
'sinh',
|
|
||||||
'snprintf',
|
|
||||||
'sprintf',
|
|
||||||
'sqrt',
|
|
||||||
'sscanf',
|
|
||||||
'std',
|
|
||||||
'stderr',
|
|
||||||
'stdin',
|
|
||||||
'stdout',
|
|
||||||
'strcat',
|
|
||||||
'strchr',
|
|
||||||
'strcmp',
|
|
||||||
'strcpy',
|
|
||||||
'strcspn',
|
|
||||||
'strlen',
|
|
||||||
'strncat',
|
|
||||||
'strncmp',
|
|
||||||
'strncpy',
|
|
||||||
'strpbrk',
|
|
||||||
'strrchr',
|
|
||||||
'strspn',
|
|
||||||
'strstr',
|
|
||||||
'swap',
|
|
||||||
'tan',
|
|
||||||
'tanh',
|
|
||||||
'terminate',
|
|
||||||
'to_underlying',
|
|
||||||
'tolower',
|
|
||||||
'toupper',
|
|
||||||
'vfprintf',
|
|
||||||
'visit',
|
|
||||||
'vprintf',
|
|
||||||
'vsprintf'
|
|
||||||
];
|
|
||||||
|
|
||||||
const LITERALS = [
|
|
||||||
'NULL',
|
|
||||||
'false',
|
|
||||||
'nullopt',
|
|
||||||
'nullptr',
|
|
||||||
'true'
|
|
||||||
];
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/keyword
|
|
||||||
const BUILT_IN = [ '_Pragma' ];
|
|
||||||
|
|
||||||
const CPP_KEYWORDS = {
|
|
||||||
type: RESERVED_TYPES,
|
|
||||||
keyword: RESERVED_KEYWORDS,
|
|
||||||
literal: LITERALS,
|
|
||||||
built_in: BUILT_IN,
|
|
||||||
_type_hints: TYPE_HINTS
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_DISPATCH = {
|
|
||||||
className: 'function.dispatch',
|
|
||||||
relevance: 0,
|
|
||||||
keywords: {
|
|
||||||
// Only for relevance, not highlighting.
|
|
||||||
_hint: FUNCTION_HINTS },
|
|
||||||
begin: regex.concat(
|
|
||||||
/\b/,
|
|
||||||
/(?!decltype)/,
|
|
||||||
/(?!if)/,
|
|
||||||
/(?!for)/,
|
|
||||||
/(?!switch)/,
|
|
||||||
/(?!while)/,
|
|
||||||
hljs.IDENT_RE,
|
|
||||||
regex.lookahead(/(<[^<>]+>|)\s*\(/))
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXPRESSION_CONTAINS = [
|
|
||||||
FUNCTION_DISPATCH,
|
|
||||||
PREPROCESSOR,
|
|
||||||
CPP_PRIMITIVE_TYPES,
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
NUMBERS,
|
|
||||||
STRINGS
|
|
||||||
];
|
|
||||||
|
|
||||||
const EXPRESSION_CONTEXT = {
|
|
||||||
// This mode covers expression context where we can't expect a function
|
|
||||||
// definition and shouldn't highlight anything that looks like one:
|
|
||||||
// `return some()`, `else if()`, `(x*sum(1, 2))`
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: /=/,
|
|
||||||
end: /;/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginKeywords: 'new throw return else',
|
|
||||||
end: /;/
|
|
||||||
}
|
|
||||||
],
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
contains: EXPRESSION_CONTAINS.concat([
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
contains: EXPRESSION_CONTAINS.concat([ 'self' ]),
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_DECLARATION = {
|
|
||||||
className: 'function',
|
|
||||||
begin: '(' + FUNCTION_TYPE_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
|
|
||||||
returnBegin: true,
|
|
||||||
end: /[{;=]/,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
illegal: /[^\w\s\*&:<>.]/,
|
|
||||||
contains: [
|
|
||||||
{ // to prevent it from being confused as the function title
|
|
||||||
begin: DECLTYPE_AUTO_RE,
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: FUNCTION_TITLE,
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [ TITLE_MODE ],
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
// needed because we do not have look-behind on the below rule
|
|
||||||
// to prevent it from grabbing the final : in a :: pair
|
|
||||||
{
|
|
||||||
begin: /::/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
// initializers
|
|
||||||
{
|
|
||||||
begin: /:/,
|
|
||||||
endsWithParent: true,
|
|
||||||
contains: [
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// allow for multiple declarations, e.g.:
|
|
||||||
// extern void f(int), g(char);
|
|
||||||
{
|
|
||||||
relevance: 0,
|
|
||||||
match: /,/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS,
|
|
||||||
CPP_PRIMITIVE_TYPES,
|
|
||||||
// Count matching parentheses.
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
'self',
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS,
|
|
||||||
CPP_PRIMITIVE_TYPES
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
CPP_PRIMITIVE_TYPES,
|
|
||||||
C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
PREPROCESSOR
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'C++',
|
|
||||||
aliases: [
|
|
||||||
'cc',
|
|
||||||
'c++',
|
|
||||||
'h++',
|
|
||||||
'hpp',
|
|
||||||
'hh',
|
|
||||||
'hxx',
|
|
||||||
'cxx'
|
|
||||||
],
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
illegal: '</',
|
|
||||||
classNameAliases: { 'function.dispatch': 'built_in' },
|
|
||||||
contains: [].concat(
|
|
||||||
EXPRESSION_CONTEXT,
|
|
||||||
FUNCTION_DECLARATION,
|
|
||||||
FUNCTION_DISPATCH,
|
|
||||||
EXPRESSION_CONTAINS,
|
|
||||||
[
|
|
||||||
PREPROCESSOR,
|
|
||||||
{ // containers: ie, `vector <int> rooms (9);`
|
|
||||||
begin: '\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\s*<(?!<)',
|
|
||||||
end: '>',
|
|
||||||
keywords: CPP_KEYWORDS,
|
|
||||||
contains: [
|
|
||||||
'self',
|
|
||||||
CPP_PRIMITIVE_TYPES
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: hljs.IDENT_RE + '::',
|
|
||||||
keywords: CPP_KEYWORDS
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
// extra complexity to deal with `enum class` and `enum struct`
|
|
||||||
/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,
|
|
||||||
/\s+/,
|
|
||||||
/\w+/
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: 'keyword',
|
|
||||||
3: 'title.class'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpp;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*! `cpp` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{
|
|
||||||
const t=e.regex,a=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]
|
|
||||||
}),n="decltype\\(auto\\)",r="[a-zA-Z_]\\w*::",i="(?!struct)("+n+"|"+t.optional(r)+"[a-zA-Z_]\\w*"+t.optional("<[^<>]+>")+")",s={
|
|
||||||
className:"type",begin:"\\b[a-z\\d_]*_t\\b"},c={className:"string",variants:[{
|
|
||||||
begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{
|
|
||||||
begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
|
|
||||||
end:"'",illegal:"."},e.END_SAME_AS_BEGIN({
|
|
||||||
begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
|
|
||||||
className:"number",variants:[{
|
|
||||||
begin:"[+-]?(?:(?:[0-9](?:'?[0-9])*\\.(?:[0-9](?:'?[0-9])*)?|\\.[0-9](?:'?[0-9])*)(?:[Ee][+-]?[0-9](?:'?[0-9])*)?|[0-9](?:'?[0-9])*[Ee][+-]?[0-9](?:'?[0-9])*|0[Xx](?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*(?:\\.(?:[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)?)?|\\.[0-9A-Fa-f](?:'?[0-9A-Fa-f])*)[Pp][+-]?[0-9](?:'?[0-9])*)(?:[Ff](?:16|32|64|128)?|(BF|bf)16|[Ll]|)"
|
|
||||||
},{
|
|
||||||
begin:"[+-]?\\b(?:0[Bb][01](?:'?[01])*|0[Xx][0-9A-Fa-f](?:'?[0-9A-Fa-f])*|0(?:'?[0-7])*|[1-9](?:'?[0-9])*)(?:[Uu](?:LL?|ll?)|[Uu][Zz]?|(?:LL?|ll?)[Uu]?|[Zz][Uu]|)"
|
|
||||||
}],relevance:0},l={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
|
|
||||||
keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
|
|
||||||
},contains:[{begin:/\\\n/,relevance:0},e.inherit(c,{className:"string"}),{
|
|
||||||
className:"string",begin:/<.*?>/},a,e.C_BLOCK_COMMENT_MODE]},u={
|
|
||||||
className:"title",begin:t.optional(r)+e.IDENT_RE,relevance:0
|
|
||||||
},d=t.optional(r)+e.IDENT_RE+"\\s*\\(",_={
|
|
||||||
type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],
|
|
||||||
keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],
|
|
||||||
literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"],
|
|
||||||
_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","flat_map","flat_set","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"]
|
|
||||||
},p={className:"function.dispatch",relevance:0,keywords:{
|
|
||||||
_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"]
|
|
||||||
},
|
|
||||||
begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))
|
|
||||||
},m=[p,l,s,a,e.C_BLOCK_COMMENT_MODE,o,c],f={variants:[{begin:/=/,end:/;/},{
|
|
||||||
begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],
|
|
||||||
keywords:_,contains:m.concat([{begin:/\(/,end:/\)/,keywords:_,
|
|
||||||
contains:m.concat(["self"]),relevance:0}]),relevance:0},g={className:"function",
|
|
||||||
begin:"("+i+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,
|
|
||||||
keywords:_,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:n,keywords:_,relevance:0},{
|
|
||||||
begin:d,returnBegin:!0,contains:[u],relevance:0},{begin:/::/,relevance:0},{
|
|
||||||
begin:/:/,endsWithParent:!0,contains:[c,o]},{relevance:0,match:/,/},{
|
|
||||||
className:"params",begin:/\(/,end:/\)/,keywords:_,relevance:0,
|
|
||||||
contains:[a,e.C_BLOCK_COMMENT_MODE,c,o,s,{begin:/\(/,end:/\)/,keywords:_,
|
|
||||||
relevance:0,contains:["self",a,e.C_BLOCK_COMMENT_MODE,c,o,s]}]
|
|
||||||
},s,a,e.C_BLOCK_COMMENT_MODE,l]};return{name:"C++",
|
|
||||||
aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:_,illegal:"</",
|
|
||||||
classNameAliases:{"function.dispatch":"built_in"},
|
|
||||||
contains:[].concat(f,g,p,m,[l,{
|
|
||||||
begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array|tuple|optional|variant|function|flat_map|flat_set)\\s*<(?!<)",
|
|
||||||
end:">",keywords:_,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:_},{
|
|
||||||
match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],
|
|
||||||
className:{1:"keyword",3:"title.class"}}])}}})();export default hljsGrammar;
|
|
||||||
@@ -1,420 +0,0 @@
|
|||||||
/*! `csharp` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: C#
|
|
||||||
Author: Jason Diamond <jason@diamond.name>
|
|
||||||
Contributor: Nicolas LLOBERA <nllobera@gmail.com>, Pieter Vantorre <pietervantorre@gmail.com>, David Pine <david.pine@microsoft.com>
|
|
||||||
Website: https://docs.microsoft.com/dotnet/csharp/
|
|
||||||
Category: common
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function csharp(hljs) {
|
|
||||||
const BUILT_IN_KEYWORDS = [
|
|
||||||
'bool',
|
|
||||||
'byte',
|
|
||||||
'char',
|
|
||||||
'decimal',
|
|
||||||
'delegate',
|
|
||||||
'double',
|
|
||||||
'dynamic',
|
|
||||||
'enum',
|
|
||||||
'float',
|
|
||||||
'int',
|
|
||||||
'long',
|
|
||||||
'nint',
|
|
||||||
'nuint',
|
|
||||||
'object',
|
|
||||||
'sbyte',
|
|
||||||
'short',
|
|
||||||
'string',
|
|
||||||
'ulong',
|
|
||||||
'uint',
|
|
||||||
'ushort'
|
|
||||||
];
|
|
||||||
const FUNCTION_MODIFIERS = [
|
|
||||||
'public',
|
|
||||||
'private',
|
|
||||||
'protected',
|
|
||||||
'static',
|
|
||||||
'internal',
|
|
||||||
'protected',
|
|
||||||
'abstract',
|
|
||||||
'async',
|
|
||||||
'extern',
|
|
||||||
'override',
|
|
||||||
'unsafe',
|
|
||||||
'virtual',
|
|
||||||
'new',
|
|
||||||
'sealed',
|
|
||||||
'partial'
|
|
||||||
];
|
|
||||||
const LITERAL_KEYWORDS = [
|
|
||||||
'default',
|
|
||||||
'false',
|
|
||||||
'null',
|
|
||||||
'true'
|
|
||||||
];
|
|
||||||
const NORMAL_KEYWORDS = [
|
|
||||||
'abstract',
|
|
||||||
'as',
|
|
||||||
'base',
|
|
||||||
'break',
|
|
||||||
'case',
|
|
||||||
'catch',
|
|
||||||
'class',
|
|
||||||
'const',
|
|
||||||
'continue',
|
|
||||||
'do',
|
|
||||||
'else',
|
|
||||||
'event',
|
|
||||||
'explicit',
|
|
||||||
'extern',
|
|
||||||
'finally',
|
|
||||||
'fixed',
|
|
||||||
'for',
|
|
||||||
'foreach',
|
|
||||||
'goto',
|
|
||||||
'if',
|
|
||||||
'implicit',
|
|
||||||
'in',
|
|
||||||
'interface',
|
|
||||||
'internal',
|
|
||||||
'is',
|
|
||||||
'lock',
|
|
||||||
'namespace',
|
|
||||||
'new',
|
|
||||||
'operator',
|
|
||||||
'out',
|
|
||||||
'override',
|
|
||||||
'params',
|
|
||||||
'private',
|
|
||||||
'protected',
|
|
||||||
'public',
|
|
||||||
'readonly',
|
|
||||||
'record',
|
|
||||||
'ref',
|
|
||||||
'return',
|
|
||||||
'scoped',
|
|
||||||
'sealed',
|
|
||||||
'sizeof',
|
|
||||||
'stackalloc',
|
|
||||||
'static',
|
|
||||||
'struct',
|
|
||||||
'switch',
|
|
||||||
'this',
|
|
||||||
'throw',
|
|
||||||
'try',
|
|
||||||
'typeof',
|
|
||||||
'unchecked',
|
|
||||||
'unsafe',
|
|
||||||
'using',
|
|
||||||
'virtual',
|
|
||||||
'void',
|
|
||||||
'volatile',
|
|
||||||
'while'
|
|
||||||
];
|
|
||||||
const CONTEXTUAL_KEYWORDS = [
|
|
||||||
'add',
|
|
||||||
'alias',
|
|
||||||
'and',
|
|
||||||
'ascending',
|
|
||||||
'args',
|
|
||||||
'async',
|
|
||||||
'await',
|
|
||||||
'by',
|
|
||||||
'descending',
|
|
||||||
'dynamic',
|
|
||||||
'equals',
|
|
||||||
'file',
|
|
||||||
'from',
|
|
||||||
'get',
|
|
||||||
'global',
|
|
||||||
'group',
|
|
||||||
'init',
|
|
||||||
'into',
|
|
||||||
'join',
|
|
||||||
'let',
|
|
||||||
'nameof',
|
|
||||||
'not',
|
|
||||||
'notnull',
|
|
||||||
'on',
|
|
||||||
'or',
|
|
||||||
'orderby',
|
|
||||||
'partial',
|
|
||||||
'record',
|
|
||||||
'remove',
|
|
||||||
'required',
|
|
||||||
'scoped',
|
|
||||||
'select',
|
|
||||||
'set',
|
|
||||||
'unmanaged',
|
|
||||||
'value|0',
|
|
||||||
'var',
|
|
||||||
'when',
|
|
||||||
'where',
|
|
||||||
'with',
|
|
||||||
'yield'
|
|
||||||
];
|
|
||||||
|
|
||||||
const KEYWORDS = {
|
|
||||||
keyword: NORMAL_KEYWORDS.concat(CONTEXTUAL_KEYWORDS),
|
|
||||||
built_in: BUILT_IN_KEYWORDS,
|
|
||||||
literal: LITERAL_KEYWORDS
|
|
||||||
};
|
|
||||||
const TITLE_MODE = hljs.inherit(hljs.TITLE_MODE, { begin: '[a-zA-Z](\\.?\\w)*' });
|
|
||||||
const NUMBERS = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
{ begin: '\\b(0b[01\']+)' },
|
|
||||||
{ begin: '(-?)\\b([\\d\']+(\\.[\\d\']*)?|\\.[\\d\']+)(u|U|l|L|ul|UL|f|F|b|B)' },
|
|
||||||
{ begin: '(-?)(\\b0[xX][a-fA-F0-9\']+|(\\b[\\d\']+(\\.[\\d\']*)?|\\.[\\d\']+)([eE][-+]?[\\d\']+)?)' }
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
const RAW_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /"""("*)(?!")(.|\n)*?"""\1/,
|
|
||||||
relevance: 1
|
|
||||||
};
|
|
||||||
const VERBATIM_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: '@"',
|
|
||||||
end: '"',
|
|
||||||
contains: [ { begin: '""' } ]
|
|
||||||
};
|
|
||||||
const VERBATIM_STRING_NO_LF = hljs.inherit(VERBATIM_STRING, { illegal: /\n/ });
|
|
||||||
const SUBST = {
|
|
||||||
className: 'subst',
|
|
||||||
begin: /\{/,
|
|
||||||
end: /\}/,
|
|
||||||
keywords: KEYWORDS
|
|
||||||
};
|
|
||||||
const SUBST_NO_LF = hljs.inherit(SUBST, { illegal: /\n/ });
|
|
||||||
const INTERPOLATED_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /\$"/,
|
|
||||||
end: '"',
|
|
||||||
illegal: /\n/,
|
|
||||||
contains: [
|
|
||||||
{ begin: /\{\{/ },
|
|
||||||
{ begin: /\}\}/ },
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
SUBST_NO_LF
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const INTERPOLATED_VERBATIM_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /\$@"/,
|
|
||||||
end: '"',
|
|
||||||
contains: [
|
|
||||||
{ begin: /\{\{/ },
|
|
||||||
{ begin: /\}\}/ },
|
|
||||||
{ begin: '""' },
|
|
||||||
SUBST
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const INTERPOLATED_VERBATIM_STRING_NO_LF = hljs.inherit(INTERPOLATED_VERBATIM_STRING, {
|
|
||||||
illegal: /\n/,
|
|
||||||
contains: [
|
|
||||||
{ begin: /\{\{/ },
|
|
||||||
{ begin: /\}\}/ },
|
|
||||||
{ begin: '""' },
|
|
||||||
SUBST_NO_LF
|
|
||||||
]
|
|
||||||
});
|
|
||||||
SUBST.contains = [
|
|
||||||
INTERPOLATED_VERBATIM_STRING,
|
|
||||||
INTERPOLATED_STRING,
|
|
||||||
VERBATIM_STRING,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
NUMBERS,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
];
|
|
||||||
SUBST_NO_LF.contains = [
|
|
||||||
INTERPOLATED_VERBATIM_STRING_NO_LF,
|
|
||||||
INTERPOLATED_STRING,
|
|
||||||
VERBATIM_STRING_NO_LF,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
NUMBERS,
|
|
||||||
hljs.inherit(hljs.C_BLOCK_COMMENT_MODE, { illegal: /\n/ })
|
|
||||||
];
|
|
||||||
const STRING = { variants: [
|
|
||||||
RAW_STRING,
|
|
||||||
INTERPOLATED_VERBATIM_STRING,
|
|
||||||
INTERPOLATED_STRING,
|
|
||||||
VERBATIM_STRING,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE
|
|
||||||
] };
|
|
||||||
|
|
||||||
const GENERIC_MODIFIER = {
|
|
||||||
begin: "<",
|
|
||||||
end: ">",
|
|
||||||
contains: [
|
|
||||||
{ beginKeywords: "in out" },
|
|
||||||
TITLE_MODE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const TYPE_IDENT_RE = hljs.IDENT_RE + '(<' + hljs.IDENT_RE + '(\\s*,\\s*' + hljs.IDENT_RE + ')*>)?(\\[\\])?';
|
|
||||||
const AT_IDENTIFIER = {
|
|
||||||
// prevents expressions like `@class` from incorrect flagging
|
|
||||||
// `class` as a keyword
|
|
||||||
begin: "@" + hljs.IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'C#',
|
|
||||||
aliases: [
|
|
||||||
'cs',
|
|
||||||
'c#'
|
|
||||||
],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: /::/,
|
|
||||||
contains: [
|
|
||||||
hljs.COMMENT(
|
|
||||||
'///',
|
|
||||||
'$',
|
|
||||||
{
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'doctag',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '///',
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{ begin: '<!--|-->' },
|
|
||||||
{
|
|
||||||
begin: '</?',
|
|
||||||
end: '>'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
{
|
|
||||||
className: 'meta',
|
|
||||||
begin: '#',
|
|
||||||
end: '$',
|
|
||||||
keywords: { keyword: 'if else elif endif define undef warning error line region endregion pragma checksum' }
|
|
||||||
},
|
|
||||||
STRING,
|
|
||||||
NUMBERS,
|
|
||||||
{
|
|
||||||
beginKeywords: 'class interface',
|
|
||||||
relevance: 0,
|
|
||||||
end: /[{;=]/,
|
|
||||||
illegal: /[^\s:,]/,
|
|
||||||
contains: [
|
|
||||||
{ beginKeywords: "where class" },
|
|
||||||
TITLE_MODE,
|
|
||||||
GENERIC_MODIFIER,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginKeywords: 'namespace',
|
|
||||||
relevance: 0,
|
|
||||||
end: /[{;=]/,
|
|
||||||
illegal: /[^\s:]/,
|
|
||||||
contains: [
|
|
||||||
TITLE_MODE,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginKeywords: 'record',
|
|
||||||
relevance: 0,
|
|
||||||
end: /[{;=]/,
|
|
||||||
illegal: /[^\s:]/,
|
|
||||||
contains: [
|
|
||||||
TITLE_MODE,
|
|
||||||
GENERIC_MODIFIER,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// [Attributes("")]
|
|
||||||
className: 'meta',
|
|
||||||
begin: '^\\s*\\[(?=[\\w])',
|
|
||||||
excludeBegin: true,
|
|
||||||
end: '\\]',
|
|
||||||
excludeEnd: true,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
begin: /"/,
|
|
||||||
end: /"/
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Expression keywords prevent 'keyword Name(...)' from being
|
|
||||||
// recognized as a function definition
|
|
||||||
beginKeywords: 'new return throw await else',
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'function',
|
|
||||||
begin: '(' + TYPE_IDENT_RE + '\\s+)+' + hljs.IDENT_RE + '\\s*(<[^=]+>\\s*)?\\(',
|
|
||||||
returnBegin: true,
|
|
||||||
end: /\s*[{;=]/,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
contains: [
|
|
||||||
// prevents these from being highlighted `title`
|
|
||||||
{
|
|
||||||
beginKeywords: FUNCTION_MODIFIERS.join(" "),
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: hljs.IDENT_RE + '\\s*(<[^=]+>\\s*)?\\(',
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [
|
|
||||||
hljs.TITLE_MODE,
|
|
||||||
GENERIC_MODIFIER
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{ match: /\(\)/ },
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
STRING,
|
|
||||||
NUMBERS,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
AT_IDENTIFIER
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return csharp;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/*! `csharp` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const n={
|
|
||||||
keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","args","async","await","by","descending","dynamic","equals","file","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","record","remove","required","scoped","select","set","unmanaged","value|0","var","when","where","with","yield"]),
|
|
||||||
built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"],
|
|
||||||
literal:["default","false","null","true"]},a=e.inherit(e.TITLE_MODE,{
|
|
||||||
begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{
|
|
||||||
begin:"\\b(0b[01']+)"},{
|
|
||||||
begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{
|
|
||||||
begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
|
|
||||||
}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]
|
|
||||||
},r=e.inherit(s,{illegal:/\n/}),t={className:"subst",begin:/\{/,end:/\}/,
|
|
||||||
keywords:n},l=e.inherit(t,{illegal:/\n/}),c={className:"string",begin:/\$"/,
|
|
||||||
end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/
|
|
||||||
},e.BACKSLASH_ESCAPE,l]},o={className:"string",begin:/\$@"/,end:'"',contains:[{
|
|
||||||
begin:/\{\{/},{begin:/\}\}/},{begin:'""'},t]},d=e.inherit(o,{illegal:/\n/,
|
|
||||||
contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},l]})
|
|
||||||
;t.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.C_BLOCK_COMMENT_MODE],
|
|
||||||
l.contains=[d,c,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,i,e.inherit(e.C_BLOCK_COMMENT_MODE,{
|
|
||||||
illegal:/\n/})];const g={variants:[{className:"string",
|
|
||||||
begin:/"""("*)(?!")(.|\n)*?"""\1/,relevance:1
|
|
||||||
},o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",
|
|
||||||
contains:[{beginKeywords:"in out"},a]
|
|
||||||
},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={
|
|
||||||
begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],
|
|
||||||
keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,
|
|
||||||
contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{
|
|
||||||
begin:"\x3c!--|--\x3e"},{begin:"</?",end:">"}]}]
|
|
||||||
}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",
|
|
||||||
end:"$",keywords:{
|
|
||||||
keyword:"if else elif endif define undef warning error line region endregion pragma checksum"
|
|
||||||
}},g,i,{beginKeywords:"class interface",relevance:0,end:/[{;=]/,
|
|
||||||
illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"
|
|
||||||
},a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",
|
|
||||||
relevance:0,end:/[{;=]/,illegal:/[^\s:]/,
|
|
||||||
contains:[a,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{
|
|
||||||
beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/,
|
|
||||||
contains:[a,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",
|
|
||||||
begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{
|
|
||||||
className:"string",begin:/"/,end:/"/}]},{
|
|
||||||
beginKeywords:"new return throw await else",relevance:0},{className:"function",
|
|
||||||
begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,
|
|
||||||
end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{
|
|
||||||
beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial",
|
|
||||||
relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0,
|
|
||||||
contains:[e.TITLE_MODE,E],relevance:0},{match:/\(\)/},{className:"params",
|
|
||||||
begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,
|
|
||||||
contains:[g,i,e.C_BLOCK_COMMENT_MODE]
|
|
||||||
},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,957 +0,0 @@
|
|||||||
/*! `css` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const MODES = (hljs) => {
|
|
||||||
return {
|
|
||||||
IMPORTANT: {
|
|
||||||
scope: 'meta',
|
|
||||||
begin: '!important'
|
|
||||||
},
|
|
||||||
BLOCK_COMMENT: hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
HEXCOLOR: {
|
|
||||||
scope: 'number',
|
|
||||||
begin: /#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/
|
|
||||||
},
|
|
||||||
FUNCTION_DISPATCH: {
|
|
||||||
className: "built_in",
|
|
||||||
begin: /[\w-]+(?=\()/
|
|
||||||
},
|
|
||||||
ATTRIBUTE_SELECTOR_MODE: {
|
|
||||||
scope: 'selector-attr',
|
|
||||||
begin: /\[/,
|
|
||||||
end: /\]/,
|
|
||||||
illegal: '$',
|
|
||||||
contains: [
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
CSS_NUMBER_MODE: {
|
|
||||||
scope: 'number',
|
|
||||||
begin: hljs.NUMBER_RE + '(' +
|
|
||||||
'%|em|ex|ch|rem' +
|
|
||||||
'|vw|vh|vmin|vmax' +
|
|
||||||
'|cm|mm|in|pt|pc|px' +
|
|
||||||
'|deg|grad|rad|turn' +
|
|
||||||
'|s|ms' +
|
|
||||||
'|Hz|kHz' +
|
|
||||||
'|dpi|dpcm|dppx' +
|
|
||||||
')?',
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
CSS_VARIABLE: {
|
|
||||||
className: "attr",
|
|
||||||
begin: /--[A-Za-z_][A-Za-z0-9_-]*/
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const HTML_TAGS = [
|
|
||||||
'a',
|
|
||||||
'abbr',
|
|
||||||
'address',
|
|
||||||
'article',
|
|
||||||
'aside',
|
|
||||||
'audio',
|
|
||||||
'b',
|
|
||||||
'blockquote',
|
|
||||||
'body',
|
|
||||||
'button',
|
|
||||||
'canvas',
|
|
||||||
'caption',
|
|
||||||
'cite',
|
|
||||||
'code',
|
|
||||||
'dd',
|
|
||||||
'del',
|
|
||||||
'details',
|
|
||||||
'dfn',
|
|
||||||
'div',
|
|
||||||
'dl',
|
|
||||||
'dt',
|
|
||||||
'em',
|
|
||||||
'fieldset',
|
|
||||||
'figcaption',
|
|
||||||
'figure',
|
|
||||||
'footer',
|
|
||||||
'form',
|
|
||||||
'h1',
|
|
||||||
'h2',
|
|
||||||
'h3',
|
|
||||||
'h4',
|
|
||||||
'h5',
|
|
||||||
'h6',
|
|
||||||
'header',
|
|
||||||
'hgroup',
|
|
||||||
'html',
|
|
||||||
'i',
|
|
||||||
'iframe',
|
|
||||||
'img',
|
|
||||||
'input',
|
|
||||||
'ins',
|
|
||||||
'kbd',
|
|
||||||
'label',
|
|
||||||
'legend',
|
|
||||||
'li',
|
|
||||||
'main',
|
|
||||||
'mark',
|
|
||||||
'menu',
|
|
||||||
'nav',
|
|
||||||
'object',
|
|
||||||
'ol',
|
|
||||||
'optgroup',
|
|
||||||
'option',
|
|
||||||
'p',
|
|
||||||
'picture',
|
|
||||||
'q',
|
|
||||||
'quote',
|
|
||||||
'samp',
|
|
||||||
'section',
|
|
||||||
'select',
|
|
||||||
'source',
|
|
||||||
'span',
|
|
||||||
'strong',
|
|
||||||
'summary',
|
|
||||||
'sup',
|
|
||||||
'table',
|
|
||||||
'tbody',
|
|
||||||
'td',
|
|
||||||
'textarea',
|
|
||||||
'tfoot',
|
|
||||||
'th',
|
|
||||||
'thead',
|
|
||||||
'time',
|
|
||||||
'tr',
|
|
||||||
'ul',
|
|
||||||
'var',
|
|
||||||
'video'
|
|
||||||
];
|
|
||||||
|
|
||||||
const SVG_TAGS = [
|
|
||||||
'defs',
|
|
||||||
'g',
|
|
||||||
'marker',
|
|
||||||
'mask',
|
|
||||||
'pattern',
|
|
||||||
'svg',
|
|
||||||
'switch',
|
|
||||||
'symbol',
|
|
||||||
'feBlend',
|
|
||||||
'feColorMatrix',
|
|
||||||
'feComponentTransfer',
|
|
||||||
'feComposite',
|
|
||||||
'feConvolveMatrix',
|
|
||||||
'feDiffuseLighting',
|
|
||||||
'feDisplacementMap',
|
|
||||||
'feFlood',
|
|
||||||
'feGaussianBlur',
|
|
||||||
'feImage',
|
|
||||||
'feMerge',
|
|
||||||
'feMorphology',
|
|
||||||
'feOffset',
|
|
||||||
'feSpecularLighting',
|
|
||||||
'feTile',
|
|
||||||
'feTurbulence',
|
|
||||||
'linearGradient',
|
|
||||||
'radialGradient',
|
|
||||||
'stop',
|
|
||||||
'circle',
|
|
||||||
'ellipse',
|
|
||||||
'image',
|
|
||||||
'line',
|
|
||||||
'path',
|
|
||||||
'polygon',
|
|
||||||
'polyline',
|
|
||||||
'rect',
|
|
||||||
'text',
|
|
||||||
'use',
|
|
||||||
'textPath',
|
|
||||||
'tspan',
|
|
||||||
'foreignObject',
|
|
||||||
'clipPath'
|
|
||||||
];
|
|
||||||
|
|
||||||
const TAGS = [
|
|
||||||
...HTML_TAGS,
|
|
||||||
...SVG_TAGS,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Sorting, then reversing makes sure longer attributes/elements like
|
|
||||||
// `font-weight` are matched fully instead of getting false positives on say `font`
|
|
||||||
|
|
||||||
const MEDIA_FEATURES = [
|
|
||||||
'any-hover',
|
|
||||||
'any-pointer',
|
|
||||||
'aspect-ratio',
|
|
||||||
'color',
|
|
||||||
'color-gamut',
|
|
||||||
'color-index',
|
|
||||||
'device-aspect-ratio',
|
|
||||||
'device-height',
|
|
||||||
'device-width',
|
|
||||||
'display-mode',
|
|
||||||
'forced-colors',
|
|
||||||
'grid',
|
|
||||||
'height',
|
|
||||||
'hover',
|
|
||||||
'inverted-colors',
|
|
||||||
'monochrome',
|
|
||||||
'orientation',
|
|
||||||
'overflow-block',
|
|
||||||
'overflow-inline',
|
|
||||||
'pointer',
|
|
||||||
'prefers-color-scheme',
|
|
||||||
'prefers-contrast',
|
|
||||||
'prefers-reduced-motion',
|
|
||||||
'prefers-reduced-transparency',
|
|
||||||
'resolution',
|
|
||||||
'scan',
|
|
||||||
'scripting',
|
|
||||||
'update',
|
|
||||||
'width',
|
|
||||||
// TODO: find a better solution?
|
|
||||||
'min-width',
|
|
||||||
'max-width',
|
|
||||||
'min-height',
|
|
||||||
'max-height'
|
|
||||||
].sort().reverse();
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
|
|
||||||
const PSEUDO_CLASSES = [
|
|
||||||
'active',
|
|
||||||
'any-link',
|
|
||||||
'blank',
|
|
||||||
'checked',
|
|
||||||
'current',
|
|
||||||
'default',
|
|
||||||
'defined',
|
|
||||||
'dir', // dir()
|
|
||||||
'disabled',
|
|
||||||
'drop',
|
|
||||||
'empty',
|
|
||||||
'enabled',
|
|
||||||
'first',
|
|
||||||
'first-child',
|
|
||||||
'first-of-type',
|
|
||||||
'fullscreen',
|
|
||||||
'future',
|
|
||||||
'focus',
|
|
||||||
'focus-visible',
|
|
||||||
'focus-within',
|
|
||||||
'has', // has()
|
|
||||||
'host', // host or host()
|
|
||||||
'host-context', // host-context()
|
|
||||||
'hover',
|
|
||||||
'indeterminate',
|
|
||||||
'in-range',
|
|
||||||
'invalid',
|
|
||||||
'is', // is()
|
|
||||||
'lang', // lang()
|
|
||||||
'last-child',
|
|
||||||
'last-of-type',
|
|
||||||
'left',
|
|
||||||
'link',
|
|
||||||
'local-link',
|
|
||||||
'not', // not()
|
|
||||||
'nth-child', // nth-child()
|
|
||||||
'nth-col', // nth-col()
|
|
||||||
'nth-last-child', // nth-last-child()
|
|
||||||
'nth-last-col', // nth-last-col()
|
|
||||||
'nth-last-of-type', //nth-last-of-type()
|
|
||||||
'nth-of-type', //nth-of-type()
|
|
||||||
'only-child',
|
|
||||||
'only-of-type',
|
|
||||||
'optional',
|
|
||||||
'out-of-range',
|
|
||||||
'past',
|
|
||||||
'placeholder-shown',
|
|
||||||
'read-only',
|
|
||||||
'read-write',
|
|
||||||
'required',
|
|
||||||
'right',
|
|
||||||
'root',
|
|
||||||
'scope',
|
|
||||||
'target',
|
|
||||||
'target-within',
|
|
||||||
'user-invalid',
|
|
||||||
'valid',
|
|
||||||
'visited',
|
|
||||||
'where' // where()
|
|
||||||
].sort().reverse();
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
|
|
||||||
const PSEUDO_ELEMENTS = [
|
|
||||||
'after',
|
|
||||||
'backdrop',
|
|
||||||
'before',
|
|
||||||
'cue',
|
|
||||||
'cue-region',
|
|
||||||
'first-letter',
|
|
||||||
'first-line',
|
|
||||||
'grammar-error',
|
|
||||||
'marker',
|
|
||||||
'part',
|
|
||||||
'placeholder',
|
|
||||||
'selection',
|
|
||||||
'slotted',
|
|
||||||
'spelling-error'
|
|
||||||
].sort().reverse();
|
|
||||||
|
|
||||||
const ATTRIBUTES = [
|
|
||||||
'accent-color',
|
|
||||||
'align-content',
|
|
||||||
'align-items',
|
|
||||||
'align-self',
|
|
||||||
'alignment-baseline',
|
|
||||||
'all',
|
|
||||||
'anchor-name',
|
|
||||||
'animation',
|
|
||||||
'animation-composition',
|
|
||||||
'animation-delay',
|
|
||||||
'animation-direction',
|
|
||||||
'animation-duration',
|
|
||||||
'animation-fill-mode',
|
|
||||||
'animation-iteration-count',
|
|
||||||
'animation-name',
|
|
||||||
'animation-play-state',
|
|
||||||
'animation-range',
|
|
||||||
'animation-range-end',
|
|
||||||
'animation-range-start',
|
|
||||||
'animation-timeline',
|
|
||||||
'animation-timing-function',
|
|
||||||
'appearance',
|
|
||||||
'aspect-ratio',
|
|
||||||
'backdrop-filter',
|
|
||||||
'backface-visibility',
|
|
||||||
'background',
|
|
||||||
'background-attachment',
|
|
||||||
'background-blend-mode',
|
|
||||||
'background-clip',
|
|
||||||
'background-color',
|
|
||||||
'background-image',
|
|
||||||
'background-origin',
|
|
||||||
'background-position',
|
|
||||||
'background-position-x',
|
|
||||||
'background-position-y',
|
|
||||||
'background-repeat',
|
|
||||||
'background-size',
|
|
||||||
'baseline-shift',
|
|
||||||
'block-size',
|
|
||||||
'border',
|
|
||||||
'border-block',
|
|
||||||
'border-block-color',
|
|
||||||
'border-block-end',
|
|
||||||
'border-block-end-color',
|
|
||||||
'border-block-end-style',
|
|
||||||
'border-block-end-width',
|
|
||||||
'border-block-start',
|
|
||||||
'border-block-start-color',
|
|
||||||
'border-block-start-style',
|
|
||||||
'border-block-start-width',
|
|
||||||
'border-block-style',
|
|
||||||
'border-block-width',
|
|
||||||
'border-bottom',
|
|
||||||
'border-bottom-color',
|
|
||||||
'border-bottom-left-radius',
|
|
||||||
'border-bottom-right-radius',
|
|
||||||
'border-bottom-style',
|
|
||||||
'border-bottom-width',
|
|
||||||
'border-collapse',
|
|
||||||
'border-color',
|
|
||||||
'border-end-end-radius',
|
|
||||||
'border-end-start-radius',
|
|
||||||
'border-image',
|
|
||||||
'border-image-outset',
|
|
||||||
'border-image-repeat',
|
|
||||||
'border-image-slice',
|
|
||||||
'border-image-source',
|
|
||||||
'border-image-width',
|
|
||||||
'border-inline',
|
|
||||||
'border-inline-color',
|
|
||||||
'border-inline-end',
|
|
||||||
'border-inline-end-color',
|
|
||||||
'border-inline-end-style',
|
|
||||||
'border-inline-end-width',
|
|
||||||
'border-inline-start',
|
|
||||||
'border-inline-start-color',
|
|
||||||
'border-inline-start-style',
|
|
||||||
'border-inline-start-width',
|
|
||||||
'border-inline-style',
|
|
||||||
'border-inline-width',
|
|
||||||
'border-left',
|
|
||||||
'border-left-color',
|
|
||||||
'border-left-style',
|
|
||||||
'border-left-width',
|
|
||||||
'border-radius',
|
|
||||||
'border-right',
|
|
||||||
'border-right-color',
|
|
||||||
'border-right-style',
|
|
||||||
'border-right-width',
|
|
||||||
'border-spacing',
|
|
||||||
'border-start-end-radius',
|
|
||||||
'border-start-start-radius',
|
|
||||||
'border-style',
|
|
||||||
'border-top',
|
|
||||||
'border-top-color',
|
|
||||||
'border-top-left-radius',
|
|
||||||
'border-top-right-radius',
|
|
||||||
'border-top-style',
|
|
||||||
'border-top-width',
|
|
||||||
'border-width',
|
|
||||||
'bottom',
|
|
||||||
'box-align',
|
|
||||||
'box-decoration-break',
|
|
||||||
'box-direction',
|
|
||||||
'box-flex',
|
|
||||||
'box-flex-group',
|
|
||||||
'box-lines',
|
|
||||||
'box-ordinal-group',
|
|
||||||
'box-orient',
|
|
||||||
'box-pack',
|
|
||||||
'box-shadow',
|
|
||||||
'box-sizing',
|
|
||||||
'break-after',
|
|
||||||
'break-before',
|
|
||||||
'break-inside',
|
|
||||||
'caption-side',
|
|
||||||
'caret-color',
|
|
||||||
'clear',
|
|
||||||
'clip',
|
|
||||||
'clip-path',
|
|
||||||
'clip-rule',
|
|
||||||
'color',
|
|
||||||
'color-interpolation',
|
|
||||||
'color-interpolation-filters',
|
|
||||||
'color-profile',
|
|
||||||
'color-rendering',
|
|
||||||
'color-scheme',
|
|
||||||
'column-count',
|
|
||||||
'column-fill',
|
|
||||||
'column-gap',
|
|
||||||
'column-rule',
|
|
||||||
'column-rule-color',
|
|
||||||
'column-rule-style',
|
|
||||||
'column-rule-width',
|
|
||||||
'column-span',
|
|
||||||
'column-width',
|
|
||||||
'columns',
|
|
||||||
'contain',
|
|
||||||
'contain-intrinsic-block-size',
|
|
||||||
'contain-intrinsic-height',
|
|
||||||
'contain-intrinsic-inline-size',
|
|
||||||
'contain-intrinsic-size',
|
|
||||||
'contain-intrinsic-width',
|
|
||||||
'container',
|
|
||||||
'container-name',
|
|
||||||
'container-type',
|
|
||||||
'content',
|
|
||||||
'content-visibility',
|
|
||||||
'counter-increment',
|
|
||||||
'counter-reset',
|
|
||||||
'counter-set',
|
|
||||||
'cue',
|
|
||||||
'cue-after',
|
|
||||||
'cue-before',
|
|
||||||
'cursor',
|
|
||||||
'cx',
|
|
||||||
'cy',
|
|
||||||
'direction',
|
|
||||||
'display',
|
|
||||||
'dominant-baseline',
|
|
||||||
'empty-cells',
|
|
||||||
'enable-background',
|
|
||||||
'field-sizing',
|
|
||||||
'fill',
|
|
||||||
'fill-opacity',
|
|
||||||
'fill-rule',
|
|
||||||
'filter',
|
|
||||||
'flex',
|
|
||||||
'flex-basis',
|
|
||||||
'flex-direction',
|
|
||||||
'flex-flow',
|
|
||||||
'flex-grow',
|
|
||||||
'flex-shrink',
|
|
||||||
'flex-wrap',
|
|
||||||
'float',
|
|
||||||
'flood-color',
|
|
||||||
'flood-opacity',
|
|
||||||
'flow',
|
|
||||||
'font',
|
|
||||||
'font-display',
|
|
||||||
'font-family',
|
|
||||||
'font-feature-settings',
|
|
||||||
'font-kerning',
|
|
||||||
'font-language-override',
|
|
||||||
'font-optical-sizing',
|
|
||||||
'font-palette',
|
|
||||||
'font-size',
|
|
||||||
'font-size-adjust',
|
|
||||||
'font-smooth',
|
|
||||||
'font-smoothing',
|
|
||||||
'font-stretch',
|
|
||||||
'font-style',
|
|
||||||
'font-synthesis',
|
|
||||||
'font-synthesis-position',
|
|
||||||
'font-synthesis-small-caps',
|
|
||||||
'font-synthesis-style',
|
|
||||||
'font-synthesis-weight',
|
|
||||||
'font-variant',
|
|
||||||
'font-variant-alternates',
|
|
||||||
'font-variant-caps',
|
|
||||||
'font-variant-east-asian',
|
|
||||||
'font-variant-emoji',
|
|
||||||
'font-variant-ligatures',
|
|
||||||
'font-variant-numeric',
|
|
||||||
'font-variant-position',
|
|
||||||
'font-variation-settings',
|
|
||||||
'font-weight',
|
|
||||||
'forced-color-adjust',
|
|
||||||
'gap',
|
|
||||||
'glyph-orientation-horizontal',
|
|
||||||
'glyph-orientation-vertical',
|
|
||||||
'grid',
|
|
||||||
'grid-area',
|
|
||||||
'grid-auto-columns',
|
|
||||||
'grid-auto-flow',
|
|
||||||
'grid-auto-rows',
|
|
||||||
'grid-column',
|
|
||||||
'grid-column-end',
|
|
||||||
'grid-column-start',
|
|
||||||
'grid-gap',
|
|
||||||
'grid-row',
|
|
||||||
'grid-row-end',
|
|
||||||
'grid-row-start',
|
|
||||||
'grid-template',
|
|
||||||
'grid-template-areas',
|
|
||||||
'grid-template-columns',
|
|
||||||
'grid-template-rows',
|
|
||||||
'hanging-punctuation',
|
|
||||||
'height',
|
|
||||||
'hyphenate-character',
|
|
||||||
'hyphenate-limit-chars',
|
|
||||||
'hyphens',
|
|
||||||
'icon',
|
|
||||||
'image-orientation',
|
|
||||||
'image-rendering',
|
|
||||||
'image-resolution',
|
|
||||||
'ime-mode',
|
|
||||||
'initial-letter',
|
|
||||||
'initial-letter-align',
|
|
||||||
'inline-size',
|
|
||||||
'inset',
|
|
||||||
'inset-area',
|
|
||||||
'inset-block',
|
|
||||||
'inset-block-end',
|
|
||||||
'inset-block-start',
|
|
||||||
'inset-inline',
|
|
||||||
'inset-inline-end',
|
|
||||||
'inset-inline-start',
|
|
||||||
'isolation',
|
|
||||||
'justify-content',
|
|
||||||
'justify-items',
|
|
||||||
'justify-self',
|
|
||||||
'kerning',
|
|
||||||
'left',
|
|
||||||
'letter-spacing',
|
|
||||||
'lighting-color',
|
|
||||||
'line-break',
|
|
||||||
'line-height',
|
|
||||||
'line-height-step',
|
|
||||||
'list-style',
|
|
||||||
'list-style-image',
|
|
||||||
'list-style-position',
|
|
||||||
'list-style-type',
|
|
||||||
'margin',
|
|
||||||
'margin-block',
|
|
||||||
'margin-block-end',
|
|
||||||
'margin-block-start',
|
|
||||||
'margin-bottom',
|
|
||||||
'margin-inline',
|
|
||||||
'margin-inline-end',
|
|
||||||
'margin-inline-start',
|
|
||||||
'margin-left',
|
|
||||||
'margin-right',
|
|
||||||
'margin-top',
|
|
||||||
'margin-trim',
|
|
||||||
'marker',
|
|
||||||
'marker-end',
|
|
||||||
'marker-mid',
|
|
||||||
'marker-start',
|
|
||||||
'marks',
|
|
||||||
'mask',
|
|
||||||
'mask-border',
|
|
||||||
'mask-border-mode',
|
|
||||||
'mask-border-outset',
|
|
||||||
'mask-border-repeat',
|
|
||||||
'mask-border-slice',
|
|
||||||
'mask-border-source',
|
|
||||||
'mask-border-width',
|
|
||||||
'mask-clip',
|
|
||||||
'mask-composite',
|
|
||||||
'mask-image',
|
|
||||||
'mask-mode',
|
|
||||||
'mask-origin',
|
|
||||||
'mask-position',
|
|
||||||
'mask-repeat',
|
|
||||||
'mask-size',
|
|
||||||
'mask-type',
|
|
||||||
'masonry-auto-flow',
|
|
||||||
'math-depth',
|
|
||||||
'math-shift',
|
|
||||||
'math-style',
|
|
||||||
'max-block-size',
|
|
||||||
'max-height',
|
|
||||||
'max-inline-size',
|
|
||||||
'max-width',
|
|
||||||
'min-block-size',
|
|
||||||
'min-height',
|
|
||||||
'min-inline-size',
|
|
||||||
'min-width',
|
|
||||||
'mix-blend-mode',
|
|
||||||
'nav-down',
|
|
||||||
'nav-index',
|
|
||||||
'nav-left',
|
|
||||||
'nav-right',
|
|
||||||
'nav-up',
|
|
||||||
'none',
|
|
||||||
'normal',
|
|
||||||
'object-fit',
|
|
||||||
'object-position',
|
|
||||||
'offset',
|
|
||||||
'offset-anchor',
|
|
||||||
'offset-distance',
|
|
||||||
'offset-path',
|
|
||||||
'offset-position',
|
|
||||||
'offset-rotate',
|
|
||||||
'opacity',
|
|
||||||
'order',
|
|
||||||
'orphans',
|
|
||||||
'outline',
|
|
||||||
'outline-color',
|
|
||||||
'outline-offset',
|
|
||||||
'outline-style',
|
|
||||||
'outline-width',
|
|
||||||
'overflow',
|
|
||||||
'overflow-anchor',
|
|
||||||
'overflow-block',
|
|
||||||
'overflow-clip-margin',
|
|
||||||
'overflow-inline',
|
|
||||||
'overflow-wrap',
|
|
||||||
'overflow-x',
|
|
||||||
'overflow-y',
|
|
||||||
'overlay',
|
|
||||||
'overscroll-behavior',
|
|
||||||
'overscroll-behavior-block',
|
|
||||||
'overscroll-behavior-inline',
|
|
||||||
'overscroll-behavior-x',
|
|
||||||
'overscroll-behavior-y',
|
|
||||||
'padding',
|
|
||||||
'padding-block',
|
|
||||||
'padding-block-end',
|
|
||||||
'padding-block-start',
|
|
||||||
'padding-bottom',
|
|
||||||
'padding-inline',
|
|
||||||
'padding-inline-end',
|
|
||||||
'padding-inline-start',
|
|
||||||
'padding-left',
|
|
||||||
'padding-right',
|
|
||||||
'padding-top',
|
|
||||||
'page',
|
|
||||||
'page-break-after',
|
|
||||||
'page-break-before',
|
|
||||||
'page-break-inside',
|
|
||||||
'paint-order',
|
|
||||||
'pause',
|
|
||||||
'pause-after',
|
|
||||||
'pause-before',
|
|
||||||
'perspective',
|
|
||||||
'perspective-origin',
|
|
||||||
'place-content',
|
|
||||||
'place-items',
|
|
||||||
'place-self',
|
|
||||||
'pointer-events',
|
|
||||||
'position',
|
|
||||||
'position-anchor',
|
|
||||||
'position-visibility',
|
|
||||||
'print-color-adjust',
|
|
||||||
'quotes',
|
|
||||||
'r',
|
|
||||||
'resize',
|
|
||||||
'rest',
|
|
||||||
'rest-after',
|
|
||||||
'rest-before',
|
|
||||||
'right',
|
|
||||||
'rotate',
|
|
||||||
'row-gap',
|
|
||||||
'ruby-align',
|
|
||||||
'ruby-position',
|
|
||||||
'scale',
|
|
||||||
'scroll-behavior',
|
|
||||||
'scroll-margin',
|
|
||||||
'scroll-margin-block',
|
|
||||||
'scroll-margin-block-end',
|
|
||||||
'scroll-margin-block-start',
|
|
||||||
'scroll-margin-bottom',
|
|
||||||
'scroll-margin-inline',
|
|
||||||
'scroll-margin-inline-end',
|
|
||||||
'scroll-margin-inline-start',
|
|
||||||
'scroll-margin-left',
|
|
||||||
'scroll-margin-right',
|
|
||||||
'scroll-margin-top',
|
|
||||||
'scroll-padding',
|
|
||||||
'scroll-padding-block',
|
|
||||||
'scroll-padding-block-end',
|
|
||||||
'scroll-padding-block-start',
|
|
||||||
'scroll-padding-bottom',
|
|
||||||
'scroll-padding-inline',
|
|
||||||
'scroll-padding-inline-end',
|
|
||||||
'scroll-padding-inline-start',
|
|
||||||
'scroll-padding-left',
|
|
||||||
'scroll-padding-right',
|
|
||||||
'scroll-padding-top',
|
|
||||||
'scroll-snap-align',
|
|
||||||
'scroll-snap-stop',
|
|
||||||
'scroll-snap-type',
|
|
||||||
'scroll-timeline',
|
|
||||||
'scroll-timeline-axis',
|
|
||||||
'scroll-timeline-name',
|
|
||||||
'scrollbar-color',
|
|
||||||
'scrollbar-gutter',
|
|
||||||
'scrollbar-width',
|
|
||||||
'shape-image-threshold',
|
|
||||||
'shape-margin',
|
|
||||||
'shape-outside',
|
|
||||||
'shape-rendering',
|
|
||||||
'speak',
|
|
||||||
'speak-as',
|
|
||||||
'src', // @font-face
|
|
||||||
'stop-color',
|
|
||||||
'stop-opacity',
|
|
||||||
'stroke',
|
|
||||||
'stroke-dasharray',
|
|
||||||
'stroke-dashoffset',
|
|
||||||
'stroke-linecap',
|
|
||||||
'stroke-linejoin',
|
|
||||||
'stroke-miterlimit',
|
|
||||||
'stroke-opacity',
|
|
||||||
'stroke-width',
|
|
||||||
'tab-size',
|
|
||||||
'table-layout',
|
|
||||||
'text-align',
|
|
||||||
'text-align-all',
|
|
||||||
'text-align-last',
|
|
||||||
'text-anchor',
|
|
||||||
'text-combine-upright',
|
|
||||||
'text-decoration',
|
|
||||||
'text-decoration-color',
|
|
||||||
'text-decoration-line',
|
|
||||||
'text-decoration-skip',
|
|
||||||
'text-decoration-skip-ink',
|
|
||||||
'text-decoration-style',
|
|
||||||
'text-decoration-thickness',
|
|
||||||
'text-emphasis',
|
|
||||||
'text-emphasis-color',
|
|
||||||
'text-emphasis-position',
|
|
||||||
'text-emphasis-style',
|
|
||||||
'text-indent',
|
|
||||||
'text-justify',
|
|
||||||
'text-orientation',
|
|
||||||
'text-overflow',
|
|
||||||
'text-rendering',
|
|
||||||
'text-shadow',
|
|
||||||
'text-size-adjust',
|
|
||||||
'text-transform',
|
|
||||||
'text-underline-offset',
|
|
||||||
'text-underline-position',
|
|
||||||
'text-wrap',
|
|
||||||
'text-wrap-mode',
|
|
||||||
'text-wrap-style',
|
|
||||||
'timeline-scope',
|
|
||||||
'top',
|
|
||||||
'touch-action',
|
|
||||||
'transform',
|
|
||||||
'transform-box',
|
|
||||||
'transform-origin',
|
|
||||||
'transform-style',
|
|
||||||
'transition',
|
|
||||||
'transition-behavior',
|
|
||||||
'transition-delay',
|
|
||||||
'transition-duration',
|
|
||||||
'transition-property',
|
|
||||||
'transition-timing-function',
|
|
||||||
'translate',
|
|
||||||
'unicode-bidi',
|
|
||||||
'user-modify',
|
|
||||||
'user-select',
|
|
||||||
'vector-effect',
|
|
||||||
'vertical-align',
|
|
||||||
'view-timeline',
|
|
||||||
'view-timeline-axis',
|
|
||||||
'view-timeline-inset',
|
|
||||||
'view-timeline-name',
|
|
||||||
'view-transition-name',
|
|
||||||
'visibility',
|
|
||||||
'voice-balance',
|
|
||||||
'voice-duration',
|
|
||||||
'voice-family',
|
|
||||||
'voice-pitch',
|
|
||||||
'voice-range',
|
|
||||||
'voice-rate',
|
|
||||||
'voice-stress',
|
|
||||||
'voice-volume',
|
|
||||||
'white-space',
|
|
||||||
'white-space-collapse',
|
|
||||||
'widows',
|
|
||||||
'width',
|
|
||||||
'will-change',
|
|
||||||
'word-break',
|
|
||||||
'word-spacing',
|
|
||||||
'word-wrap',
|
|
||||||
'writing-mode',
|
|
||||||
'x',
|
|
||||||
'y',
|
|
||||||
'z-index',
|
|
||||||
'zoom'
|
|
||||||
].sort().reverse();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: CSS
|
|
||||||
Category: common, css, web
|
|
||||||
Website: https://developer.mozilla.org/en-US/docs/Web/CSS
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function css(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const modes = MODES(hljs);
|
|
||||||
const VENDOR_PREFIX = { begin: /-(webkit|moz|ms|o)-(?=[a-z])/ };
|
|
||||||
const AT_MODIFIERS = "and or not only";
|
|
||||||
const AT_PROPERTY_RE = /@-?\w[\w]*(-\w+)*/; // @-webkit-keyframes
|
|
||||||
const IDENT_RE = '[a-zA-Z-][a-zA-Z0-9_-]*';
|
|
||||||
const STRINGS = [
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'CSS',
|
|
||||||
case_insensitive: true,
|
|
||||||
illegal: /[=|'\$]/,
|
|
||||||
keywords: { keyframePosition: "from to" },
|
|
||||||
classNameAliases: {
|
|
||||||
// for visual continuity with `tag {}` and because we
|
|
||||||
// don't have a great class for this?
|
|
||||||
keyframePosition: "selector-tag" },
|
|
||||||
contains: [
|
|
||||||
modes.BLOCK_COMMENT,
|
|
||||||
VENDOR_PREFIX,
|
|
||||||
// to recognize keyframe 40% etc which are outside the scope of our
|
|
||||||
// attribute value mode
|
|
||||||
modes.CSS_NUMBER_MODE,
|
|
||||||
{
|
|
||||||
className: 'selector-id',
|
|
||||||
begin: /#[A-Za-z0-9_-]+/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'selector-class',
|
|
||||||
begin: '\\.' + IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
modes.ATTRIBUTE_SELECTOR_MODE,
|
|
||||||
{
|
|
||||||
className: 'selector-pseudo',
|
|
||||||
variants: [
|
|
||||||
{ begin: ':(' + PSEUDO_CLASSES.join('|') + ')' },
|
|
||||||
{ begin: ':(:)?(' + PSEUDO_ELEMENTS.join('|') + ')' }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// we may actually need this (12/2020)
|
|
||||||
// { // pseudo-selector params
|
|
||||||
// begin: /\(/,
|
|
||||||
// end: /\)/,
|
|
||||||
// contains: [ hljs.CSS_NUMBER_MODE ]
|
|
||||||
// },
|
|
||||||
modes.CSS_VARIABLE,
|
|
||||||
{
|
|
||||||
className: 'attribute',
|
|
||||||
begin: '\\b(' + ATTRIBUTES.join('|') + ')\\b'
|
|
||||||
},
|
|
||||||
// attribute values
|
|
||||||
{
|
|
||||||
begin: /:/,
|
|
||||||
end: /[;}{]/,
|
|
||||||
contains: [
|
|
||||||
modes.BLOCK_COMMENT,
|
|
||||||
modes.HEXCOLOR,
|
|
||||||
modes.IMPORTANT,
|
|
||||||
modes.CSS_NUMBER_MODE,
|
|
||||||
...STRINGS,
|
|
||||||
// needed to highlight these as strings and to avoid issues with
|
|
||||||
// illegal characters that might be inside urls that would tigger the
|
|
||||||
// languages illegal stack
|
|
||||||
{
|
|
||||||
begin: /(url|data-uri)\(/,
|
|
||||||
end: /\)/,
|
|
||||||
relevance: 0, // from keywords
|
|
||||||
keywords: { built_in: "url data-uri" },
|
|
||||||
contains: [
|
|
||||||
...STRINGS,
|
|
||||||
{
|
|
||||||
className: "string",
|
|
||||||
// any character other than `)` as in `url()` will be the start
|
|
||||||
// of a string, which ends with `)` (from the parent mode)
|
|
||||||
begin: /[^)]/,
|
|
||||||
endsWithParent: true,
|
|
||||||
excludeEnd: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
modes.FUNCTION_DISPATCH
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: regex.lookahead(/@/),
|
|
||||||
end: '[{;]',
|
|
||||||
relevance: 0,
|
|
||||||
illegal: /:/, // break on Less variables @var: ...
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'keyword',
|
|
||||||
begin: AT_PROPERTY_RE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /\s/,
|
|
||||||
endsWithParent: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
relevance: 0,
|
|
||||||
keywords: {
|
|
||||||
$pattern: /[a-z-]+/,
|
|
||||||
keyword: AT_MODIFIERS,
|
|
||||||
attribute: MEDIA_FEATURES.join(" ")
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /[a-z-]+(?=:)/,
|
|
||||||
className: "attribute"
|
|
||||||
},
|
|
||||||
...STRINGS,
|
|
||||||
modes.CSS_NUMBER_MODE
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'selector-tag',
|
|
||||||
begin: '\\b(' + TAGS.join('|') + ')\\b'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return css;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,70 +0,0 @@
|
|||||||
/*! `diff` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Diff
|
|
||||||
Description: Unified and context diff
|
|
||||||
Author: Vasily Polovnyov <vast@whiteants.net>
|
|
||||||
Website: https://www.gnu.org/software/diffutils/
|
|
||||||
Category: common
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function diff(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
return {
|
|
||||||
name: 'Diff',
|
|
||||||
aliases: [ 'patch' ],
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'meta',
|
|
||||||
relevance: 10,
|
|
||||||
match: regex.either(
|
|
||||||
/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,
|
|
||||||
/^\*\*\* +\d+,\d+ +\*\*\*\*$/,
|
|
||||||
/^--- +\d+,\d+ +----$/
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'comment',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: regex.either(
|
|
||||||
/Index: /,
|
|
||||||
/^index/,
|
|
||||||
/={3,}/,
|
|
||||||
/^-{3}/,
|
|
||||||
/^\*{3} /,
|
|
||||||
/^\+{3}/,
|
|
||||||
/^diff --git/
|
|
||||||
),
|
|
||||||
end: /$/
|
|
||||||
},
|
|
||||||
{ match: /^\*{15}$/ }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'addition',
|
|
||||||
begin: /^\+/,
|
|
||||||
end: /$/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'deletion',
|
|
||||||
begin: /^-/,
|
|
||||||
end: /$/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'addition',
|
|
||||||
begin: /^!/,
|
|
||||||
end: /$/
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return diff;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/*! `diff` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const a=e.regex;return{
|
|
||||||
name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,
|
|
||||||
match:a.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)
|
|
||||||
},{className:"comment",variants:[{
|
|
||||||
begin:a.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),
|
|
||||||
end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{
|
|
||||||
className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,
|
|
||||||
end:/$/}]}}})();export default hljsGrammar;
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/*! `go` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Go
|
|
||||||
Author: Stephan Kountso aka StepLg <steplg@gmail.com>
|
|
||||||
Contributors: Evgeny Stepanischev <imbolk@gmail.com>
|
|
||||||
Description: Google go language (golang). For info about language
|
|
||||||
Website: http://golang.org/
|
|
||||||
Category: common, system
|
|
||||||
*/
|
|
||||||
|
|
||||||
function go(hljs) {
|
|
||||||
const LITERALS = [
|
|
||||||
"true",
|
|
||||||
"false",
|
|
||||||
"iota",
|
|
||||||
"nil"
|
|
||||||
];
|
|
||||||
const BUILT_INS = [
|
|
||||||
"append",
|
|
||||||
"cap",
|
|
||||||
"close",
|
|
||||||
"complex",
|
|
||||||
"copy",
|
|
||||||
"imag",
|
|
||||||
"len",
|
|
||||||
"make",
|
|
||||||
"new",
|
|
||||||
"panic",
|
|
||||||
"print",
|
|
||||||
"println",
|
|
||||||
"real",
|
|
||||||
"recover",
|
|
||||||
"delete"
|
|
||||||
];
|
|
||||||
const TYPES = [
|
|
||||||
"bool",
|
|
||||||
"byte",
|
|
||||||
"complex64",
|
|
||||||
"complex128",
|
|
||||||
"error",
|
|
||||||
"float32",
|
|
||||||
"float64",
|
|
||||||
"int8",
|
|
||||||
"int16",
|
|
||||||
"int32",
|
|
||||||
"int64",
|
|
||||||
"string",
|
|
||||||
"uint8",
|
|
||||||
"uint16",
|
|
||||||
"uint32",
|
|
||||||
"uint64",
|
|
||||||
"int",
|
|
||||||
"uint",
|
|
||||||
"uintptr",
|
|
||||||
"rune"
|
|
||||||
];
|
|
||||||
const KWS = [
|
|
||||||
"break",
|
|
||||||
"case",
|
|
||||||
"chan",
|
|
||||||
"const",
|
|
||||||
"continue",
|
|
||||||
"default",
|
|
||||||
"defer",
|
|
||||||
"else",
|
|
||||||
"fallthrough",
|
|
||||||
"for",
|
|
||||||
"func",
|
|
||||||
"go",
|
|
||||||
"goto",
|
|
||||||
"if",
|
|
||||||
"import",
|
|
||||||
"interface",
|
|
||||||
"map",
|
|
||||||
"package",
|
|
||||||
"range",
|
|
||||||
"return",
|
|
||||||
"select",
|
|
||||||
"struct",
|
|
||||||
"switch",
|
|
||||||
"type",
|
|
||||||
"var",
|
|
||||||
];
|
|
||||||
const KEYWORDS = {
|
|
||||||
keyword: KWS,
|
|
||||||
type: TYPES,
|
|
||||||
literal: LITERALS,
|
|
||||||
built_in: BUILT_INS
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
name: 'Go',
|
|
||||||
aliases: [ 'golang' ],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: '</',
|
|
||||||
contains: [
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
variants: [
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
{
|
|
||||||
begin: '`',
|
|
||||||
end: '`'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
match: /-?\b0[xX]\.[a-fA-F0-9](_?[a-fA-F0-9])*[pP][+-]?\d(_?\d)*i?/, // hex without a present digit before . (making a digit afterwards required)
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /-?\b0[xX](_?[a-fA-F0-9])+((\.([a-fA-F0-9](_?[a-fA-F0-9])*)?)?[pP][+-]?\d(_?\d)*)?i?/, // hex with a present digit before . (making a digit afterwards optional)
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /-?\b0[oO](_?[0-7])*i?/, // leading 0o octal
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /-?\.\d(_?\d)*([eE][+-]?\d(_?\d)*)?i?/, // decimal without a present digit before . (making a digit afterwards required)
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /-?\b\d(_?\d)*(\.(\d(_?\d)*)?)?([eE][+-]?\d(_?\d)*)?i?/, // decimal with a present digit before . (making a digit afterwards optional)
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ begin: /:=/ // relevance booster
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'function',
|
|
||||||
beginKeywords: 'func',
|
|
||||||
end: '\\s*(\\{|$)',
|
|
||||||
excludeEnd: true,
|
|
||||||
contains: [
|
|
||||||
hljs.TITLE_MODE,
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
endsParent: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: /["']/
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return go;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*! `go` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const a={
|
|
||||||
keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"],
|
|
||||||
type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"],
|
|
||||||
literal:["true","false","iota","nil"],
|
|
||||||
built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"]
|
|
||||||
};return{name:"Go",aliases:["golang"],keywords:a,illegal:"</",
|
|
||||||
contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",
|
|
||||||
variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:"`",end:"`"}]},{
|
|
||||||
className:"number",variants:[{
|
|
||||||
match:/-?\b0[xX]\.[a-fA-F0-9](_?[a-fA-F0-9])*[pP][+-]?\d(_?\d)*i?/,relevance:0
|
|
||||||
},{
|
|
||||||
match:/-?\b0[xX](_?[a-fA-F0-9])+((\.([a-fA-F0-9](_?[a-fA-F0-9])*)?)?[pP][+-]?\d(_?\d)*)?i?/,
|
|
||||||
relevance:0},{match:/-?\b0[oO](_?[0-7])*i?/,relevance:0},{
|
|
||||||
match:/-?\.\d(_?\d)*([eE][+-]?\d(_?\d)*)?i?/,relevance:0},{
|
|
||||||
match:/-?\b\d(_?\d)*(\.(\d(_?\d)*)?)?([eE][+-]?\d(_?\d)*)?i?/,relevance:0}]},{
|
|
||||||
begin:/:=/},{className:"function",beginKeywords:"func",end:"\\s*(\\{|$)",
|
|
||||||
excludeEnd:!0,contains:[e.TITLE_MODE,{className:"params",begin:/\(/,end:/\)/,
|
|
||||||
endsParent:!0,keywords:a,illegal:/["']/}]}]}}})();export default hljsGrammar;
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*! `graphql` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: GraphQL
|
|
||||||
Author: John Foster (GH jf990), and others
|
|
||||||
Description: GraphQL is a query language for APIs
|
|
||||||
Category: web, common
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function graphql(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const GQL_NAME = /[_A-Za-z][_0-9A-Za-z]*/;
|
|
||||||
return {
|
|
||||||
name: "GraphQL",
|
|
||||||
aliases: [ "gql" ],
|
|
||||||
case_insensitive: true,
|
|
||||||
disableAutodetect: false,
|
|
||||||
keywords: {
|
|
||||||
keyword: [
|
|
||||||
"query",
|
|
||||||
"mutation",
|
|
||||||
"subscription",
|
|
||||||
"type",
|
|
||||||
"input",
|
|
||||||
"schema",
|
|
||||||
"directive",
|
|
||||||
"interface",
|
|
||||||
"union",
|
|
||||||
"scalar",
|
|
||||||
"fragment",
|
|
||||||
"enum",
|
|
||||||
"on"
|
|
||||||
],
|
|
||||||
literal: [
|
|
||||||
"true",
|
|
||||||
"false",
|
|
||||||
"null"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
hljs.HASH_COMMENT_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
hljs.NUMBER_MODE,
|
|
||||||
{
|
|
||||||
scope: "punctuation",
|
|
||||||
match: /[.]{3}/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scope: "punctuation",
|
|
||||||
begin: /[\!\(\)\:\=\[\]\{\|\}]{1}/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scope: "variable",
|
|
||||||
begin: /\$/,
|
|
||||||
end: /\W/,
|
|
||||||
excludeEnd: true,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scope: "meta",
|
|
||||||
match: /@\w+/,
|
|
||||||
excludeEnd: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scope: "symbol",
|
|
||||||
begin: regex.concat(GQL_NAME, regex.lookahead(/\s*:/)),
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
illegal: [
|
|
||||||
/[;<']/,
|
|
||||||
/BEGIN/
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return graphql;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/*! `graphql` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const a=e.regex;return{
|
|
||||||
name:"GraphQL",aliases:["gql"],case_insensitive:!0,disableAutodetect:!1,
|
|
||||||
keywords:{
|
|
||||||
keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"],
|
|
||||||
literal:["true","false","null"]},
|
|
||||||
contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{
|
|
||||||
scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation",
|
|
||||||
begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/,
|
|
||||||
end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{
|
|
||||||
scope:"symbol",begin:a.concat(/[_A-Za-z][_0-9A-Za-z]*/,a.lookahead(/\s*:/)),
|
|
||||||
relevance:0}],illegal:[/[;<']/,/BEGIN/]}}})();export default hljsGrammar;
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
/*! `ini` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: TOML, also INI
|
|
||||||
Description: TOML aims to be a minimal configuration file format that's easy to read due to obvious semantics.
|
|
||||||
Contributors: Guillaume Gomez <guillaume1.gomez@gmail.com>
|
|
||||||
Category: common, config
|
|
||||||
Website: https://github.com/toml-lang/toml
|
|
||||||
*/
|
|
||||||
|
|
||||||
function ini(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const NUMBERS = {
|
|
||||||
className: 'number',
|
|
||||||
relevance: 0,
|
|
||||||
variants: [
|
|
||||||
{ begin: /([+-]+)?[\d]+_[\d_]+/ },
|
|
||||||
{ begin: hljs.NUMBER_RE }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const COMMENTS = hljs.COMMENT();
|
|
||||||
COMMENTS.variants = [
|
|
||||||
{
|
|
||||||
begin: /;/,
|
|
||||||
end: /$/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /#/,
|
|
||||||
end: /$/
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const VARIABLES = {
|
|
||||||
className: 'variable',
|
|
||||||
variants: [
|
|
||||||
{ begin: /\$[\w\d"][\w\d_]*/ },
|
|
||||||
{ begin: /\$\{(.*?)\}/ }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const LITERALS = {
|
|
||||||
className: 'literal',
|
|
||||||
begin: /\bon|off|true|false|yes|no\b/
|
|
||||||
};
|
|
||||||
const STRINGS = {
|
|
||||||
className: "string",
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ],
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: "'''",
|
|
||||||
end: "'''",
|
|
||||||
relevance: 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '"""',
|
|
||||||
end: '"""',
|
|
||||||
relevance: 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '"',
|
|
||||||
end: '"'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: "'",
|
|
||||||
end: "'"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const ARRAY = {
|
|
||||||
begin: /\[/,
|
|
||||||
end: /\]/,
|
|
||||||
contains: [
|
|
||||||
COMMENTS,
|
|
||||||
LITERALS,
|
|
||||||
VARIABLES,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS,
|
|
||||||
'self'
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const BARE_KEY = /[A-Za-z0-9_-]+/;
|
|
||||||
const QUOTED_KEY_DOUBLE_QUOTE = /"(\\"|[^"])*"/;
|
|
||||||
const QUOTED_KEY_SINGLE_QUOTE = /'[^']*'/;
|
|
||||||
const ANY_KEY = regex.either(
|
|
||||||
BARE_KEY, QUOTED_KEY_DOUBLE_QUOTE, QUOTED_KEY_SINGLE_QUOTE
|
|
||||||
);
|
|
||||||
const DOTTED_KEY = regex.concat(
|
|
||||||
ANY_KEY, '(\\s*\\.\\s*', ANY_KEY, ')*',
|
|
||||||
regex.lookahead(/\s*=\s*[^#\s]/)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'TOML, also INI',
|
|
||||||
aliases: [ 'toml' ],
|
|
||||||
case_insensitive: true,
|
|
||||||
illegal: /\S/,
|
|
||||||
contains: [
|
|
||||||
COMMENTS,
|
|
||||||
{
|
|
||||||
className: 'section',
|
|
||||||
begin: /\[+/,
|
|
||||||
end: /\]+/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: DOTTED_KEY,
|
|
||||||
className: 'attr',
|
|
||||||
starts: {
|
|
||||||
end: /$/,
|
|
||||||
contains: [
|
|
||||||
COMMENTS,
|
|
||||||
ARRAY,
|
|
||||||
LITERALS,
|
|
||||||
VARIABLES,
|
|
||||||
STRINGS,
|
|
||||||
NUMBERS
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ini;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
/*! `ini` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const n=e.regex,a={
|
|
||||||
className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{
|
|
||||||
begin:e.NUMBER_RE}]},s=e.COMMENT();s.variants=[{begin:/;/,end:/$/},{begin:/#/,
|
|
||||||
end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{
|
|
||||||
begin:/\$\{(.*?)\}/}]},r={className:"literal",
|
|
||||||
begin:/\bon|off|true|false|yes|no\b/},t={className:"string",
|
|
||||||
contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{
|
|
||||||
begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]
|
|
||||||
},l={begin:/\[/,end:/\]/,contains:[s,r,i,t,a,"self"],relevance:0
|
|
||||||
},c=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{
|
|
||||||
name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,
|
|
||||||
contains:[s,{className:"section",begin:/\[+/,end:/\]+/},{
|
|
||||||
begin:n.concat(c,"(\\s*\\.\\s*",c,")*",n.lookahead(/\s*=\s*[^#\s]/)),
|
|
||||||
className:"attr",starts:{end:/$/,contains:[s,l,r,i,t,a]}}]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,299 +0,0 @@
|
|||||||
/*! `java` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10
|
|
||||||
var decimalDigits = '[0-9](_*[0-9])*';
|
|
||||||
var frac = `\\.(${decimalDigits})`;
|
|
||||||
var hexDigits = '[0-9a-fA-F](_*[0-9a-fA-F])*';
|
|
||||||
var NUMERIC = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
// DecimalFloatingPointLiteral
|
|
||||||
// including ExponentPart
|
|
||||||
{ begin: `(\\b(${decimalDigits})((${frac})|\\.)?|(${frac}))` +
|
|
||||||
`[eE][+-]?(${decimalDigits})[fFdD]?\\b` },
|
|
||||||
// excluding ExponentPart
|
|
||||||
{ begin: `\\b(${decimalDigits})((${frac})[fFdD]?\\b|\\.([fFdD]\\b)?)` },
|
|
||||||
{ begin: `(${frac})[fFdD]?\\b` },
|
|
||||||
{ begin: `\\b(${decimalDigits})[fFdD]\\b` },
|
|
||||||
|
|
||||||
// HexadecimalFloatingPointLiteral
|
|
||||||
{ begin: `\\b0[xX]((${hexDigits})\\.?|(${hexDigits})?\\.(${hexDigits}))` +
|
|
||||||
`[pP][+-]?(${decimalDigits})[fFdD]?\\b` },
|
|
||||||
|
|
||||||
// DecimalIntegerLiteral
|
|
||||||
{ begin: '\\b(0|[1-9](_*[0-9])*)[lL]?\\b' },
|
|
||||||
|
|
||||||
// HexIntegerLiteral
|
|
||||||
{ begin: `\\b0[xX](${hexDigits})[lL]?\\b` },
|
|
||||||
|
|
||||||
// OctalIntegerLiteral
|
|
||||||
{ begin: '\\b0(_*[0-7])*[lL]?\\b' },
|
|
||||||
|
|
||||||
// BinaryIntegerLiteral
|
|
||||||
{ begin: '\\b0[bB][01](_*[01])*[lL]?\\b' },
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Java
|
|
||||||
Author: Vsevolod Solovyov <vsevolod.solovyov@gmail.com>
|
|
||||||
Category: common, enterprise
|
|
||||||
Website: https://www.java.com/
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows recursive regex expressions to a given depth
|
|
||||||
*
|
|
||||||
* ie: recurRegex("(abc~~~)", /~~~/g, 2) becomes:
|
|
||||||
* (abc(abc(abc)))
|
|
||||||
*
|
|
||||||
* @param {string} re
|
|
||||||
* @param {RegExp} substitution (should be a g mode regex)
|
|
||||||
* @param {number} depth
|
|
||||||
* @returns {string}``
|
|
||||||
*/
|
|
||||||
function recurRegex(re, substitution, depth) {
|
|
||||||
if (depth === -1) return "";
|
|
||||||
|
|
||||||
return re.replace(substitution, _ => {
|
|
||||||
return recurRegex(re, substitution, depth - 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function java(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const JAVA_IDENT_RE = '[\u00C0-\u02B8a-zA-Z_$][\u00C0-\u02B8a-zA-Z_$0-9]*';
|
|
||||||
const GENERIC_IDENT_RE = JAVA_IDENT_RE
|
|
||||||
+ recurRegex('(?:<' + JAVA_IDENT_RE + '~~~(?:\\s*,\\s*' + JAVA_IDENT_RE + '~~~)*>)?', /~~~/g, 2);
|
|
||||||
const MAIN_KEYWORDS = [
|
|
||||||
'synchronized',
|
|
||||||
'abstract',
|
|
||||||
'private',
|
|
||||||
'var',
|
|
||||||
'static',
|
|
||||||
'if',
|
|
||||||
'const ',
|
|
||||||
'for',
|
|
||||||
'while',
|
|
||||||
'strictfp',
|
|
||||||
'finally',
|
|
||||||
'protected',
|
|
||||||
'import',
|
|
||||||
'native',
|
|
||||||
'final',
|
|
||||||
'void',
|
|
||||||
'enum',
|
|
||||||
'else',
|
|
||||||
'break',
|
|
||||||
'transient',
|
|
||||||
'catch',
|
|
||||||
'instanceof',
|
|
||||||
'volatile',
|
|
||||||
'case',
|
|
||||||
'assert',
|
|
||||||
'package',
|
|
||||||
'default',
|
|
||||||
'public',
|
|
||||||
'try',
|
|
||||||
'switch',
|
|
||||||
'continue',
|
|
||||||
'throws',
|
|
||||||
'protected',
|
|
||||||
'public',
|
|
||||||
'private',
|
|
||||||
'module',
|
|
||||||
'requires',
|
|
||||||
'exports',
|
|
||||||
'do',
|
|
||||||
'sealed',
|
|
||||||
'yield',
|
|
||||||
'permits',
|
|
||||||
'goto',
|
|
||||||
'when'
|
|
||||||
];
|
|
||||||
|
|
||||||
const BUILT_INS = [
|
|
||||||
'super',
|
|
||||||
'this'
|
|
||||||
];
|
|
||||||
|
|
||||||
const LITERALS = [
|
|
||||||
'false',
|
|
||||||
'true',
|
|
||||||
'null'
|
|
||||||
];
|
|
||||||
|
|
||||||
const TYPES = [
|
|
||||||
'char',
|
|
||||||
'boolean',
|
|
||||||
'long',
|
|
||||||
'float',
|
|
||||||
'int',
|
|
||||||
'byte',
|
|
||||||
'short',
|
|
||||||
'double'
|
|
||||||
];
|
|
||||||
|
|
||||||
const KEYWORDS = {
|
|
||||||
keyword: MAIN_KEYWORDS,
|
|
||||||
literal: LITERALS,
|
|
||||||
type: TYPES,
|
|
||||||
built_in: BUILT_INS
|
|
||||||
};
|
|
||||||
|
|
||||||
const ANNOTATION = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: '@' + JAVA_IDENT_RE,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
contains: [ "self" ] // allow nested () inside our annotation
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const PARAMS = {
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [ hljs.C_BLOCK_COMMENT_MODE ],
|
|
||||||
endsParent: true
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Java',
|
|
||||||
aliases: [ 'jsp' ],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: /<\/|#/,
|
|
||||||
contains: [
|
|
||||||
hljs.COMMENT(
|
|
||||||
'/\\*\\*',
|
|
||||||
'\\*/',
|
|
||||||
{
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
// eat up @'s in emails to prevent them to be recognized as doctags
|
|
||||||
begin: /\w+@/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'doctag',
|
|
||||||
begin: '@[A-Za-z]+'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
// relevance boost
|
|
||||||
{
|
|
||||||
begin: /import java\.[a-z]+\./,
|
|
||||||
keywords: "import",
|
|
||||||
relevance: 2
|
|
||||||
},
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
{
|
|
||||||
begin: /"""/,
|
|
||||||
end: /"""/,
|
|
||||||
className: "string",
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
},
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/\b(?:class|interface|enum|extends|implements|new)/,
|
|
||||||
/\s+/,
|
|
||||||
JAVA_IDENT_RE
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.class"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Exceptions for hyphenated keywords
|
|
||||||
match: /non-sealed/,
|
|
||||||
scope: "keyword"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: [
|
|
||||||
regex.concat(/(?!else)/, JAVA_IDENT_RE),
|
|
||||||
/\s+/,
|
|
||||||
JAVA_IDENT_RE,
|
|
||||||
/\s+/,
|
|
||||||
/=(?!=)/
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: "type",
|
|
||||||
3: "variable",
|
|
||||||
5: "operator"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: [
|
|
||||||
/record/,
|
|
||||||
/\s+/,
|
|
||||||
JAVA_IDENT_RE
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.class"
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
PARAMS,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Expression keywords prevent 'keyword Name(...)' from being
|
|
||||||
// recognized as a function definition
|
|
||||||
beginKeywords: 'new throw return else',
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: [
|
|
||||||
'(?:' + GENERIC_IDENT_RE + '\\s+)',
|
|
||||||
hljs.UNDERSCORE_IDENT_RE,
|
|
||||||
/\s*(?=\()/
|
|
||||||
],
|
|
||||||
className: { 2: "title.function" },
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
ANNOTATION,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
NUMERIC,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
NUMERIC,
|
|
||||||
ANNOTATION
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return java;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*! `java` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict"
|
|
||||||
;var e="[0-9](_*[0-9])*",a=`\\.(${e})`,n="[0-9a-fA-F](_*[0-9a-fA-F])*",s={
|
|
||||||
className:"number",variants:[{
|
|
||||||
begin:`(\\b(${e})((${a})|\\.)?|(${a}))[eE][+-]?(${e})[fFdD]?\\b`},{
|
|
||||||
begin:`\\b(${e})((${a})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${a})[fFdD]?\\b`
|
|
||||||
},{begin:`\\b(${e})[fFdD]\\b`},{
|
|
||||||
begin:`\\b0[xX]((${n})\\.?|(${n})?\\.(${n}))[pP][+-]?(${e})[fFdD]?\\b`},{
|
|
||||||
begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${n})[lL]?\\b`},{
|
|
||||||
begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],
|
|
||||||
relevance:0};function t(e,a,n){return-1===n?"":e.replace(a,(s=>t(e,a,n-1)))}
|
|
||||||
return e=>{
|
|
||||||
const a=e.regex,n="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",r=n+t("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),i={
|
|
||||||
keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits","goto","when"],
|
|
||||||
literal:["false","true","null"],
|
|
||||||
type:["char","boolean","long","float","int","byte","short","double"],
|
|
||||||
built_in:["super","this"]},l={className:"meta",begin:"@"+n,contains:[{
|
|
||||||
begin:/\(/,end:/\)/,contains:["self"]}]},c={className:"params",begin:/\(/,
|
|
||||||
end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0}
|
|
||||||
;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/,
|
|
||||||
contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,
|
|
||||||
relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{
|
|
||||||
begin:/import java\.[a-z]+\./,keywords:"import",relevance:2
|
|
||||||
},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,
|
|
||||||
className:"string",contains:[e.BACKSLASH_ESCAPE]
|
|
||||||
},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{
|
|
||||||
match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{
|
|
||||||
1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{
|
|
||||||
begin:[a.concat(/(?!else)/,n),/\s+/,n,/\s+/,/=(?!=)/],className:{1:"type",
|
|
||||||
3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword",
|
|
||||||
3:"title.class"},contains:[c,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{
|
|
||||||
beginKeywords:"new throw return else",relevance:0},{
|
|
||||||
begin:["(?:"+r+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{
|
|
||||||
2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/,
|
|
||||||
end:/\)/,keywords:i,relevance:0,
|
|
||||||
contains:[l,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,s,e.C_BLOCK_COMMENT_MODE]
|
|
||||||
},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},s,l]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,777 +0,0 @@
|
|||||||
/*! `javascript` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
|
|
||||||
const KEYWORDS = [
|
|
||||||
"as", // for exports
|
|
||||||
"in",
|
|
||||||
"of",
|
|
||||||
"if",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"finally",
|
|
||||||
"var",
|
|
||||||
"new",
|
|
||||||
"function",
|
|
||||||
"do",
|
|
||||||
"return",
|
|
||||||
"void",
|
|
||||||
"else",
|
|
||||||
"break",
|
|
||||||
"catch",
|
|
||||||
"instanceof",
|
|
||||||
"with",
|
|
||||||
"throw",
|
|
||||||
"case",
|
|
||||||
"default",
|
|
||||||
"try",
|
|
||||||
"switch",
|
|
||||||
"continue",
|
|
||||||
"typeof",
|
|
||||||
"delete",
|
|
||||||
"let",
|
|
||||||
"yield",
|
|
||||||
"const",
|
|
||||||
"class",
|
|
||||||
// JS handles these with a special rule
|
|
||||||
// "get",
|
|
||||||
// "set",
|
|
||||||
"debugger",
|
|
||||||
"async",
|
|
||||||
"await",
|
|
||||||
"static",
|
|
||||||
"import",
|
|
||||||
"from",
|
|
||||||
"export",
|
|
||||||
"extends",
|
|
||||||
// It's reached stage 3, which is "recommended for implementation":
|
|
||||||
"using"
|
|
||||||
];
|
|
||||||
const LITERALS = [
|
|
||||||
"true",
|
|
||||||
"false",
|
|
||||||
"null",
|
|
||||||
"undefined",
|
|
||||||
"NaN",
|
|
||||||
"Infinity"
|
|
||||||
];
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
|
|
||||||
const TYPES = [
|
|
||||||
// Fundamental objects
|
|
||||||
"Object",
|
|
||||||
"Function",
|
|
||||||
"Boolean",
|
|
||||||
"Symbol",
|
|
||||||
// numbers and dates
|
|
||||||
"Math",
|
|
||||||
"Date",
|
|
||||||
"Number",
|
|
||||||
"BigInt",
|
|
||||||
// text
|
|
||||||
"String",
|
|
||||||
"RegExp",
|
|
||||||
// Indexed collections
|
|
||||||
"Array",
|
|
||||||
"Float32Array",
|
|
||||||
"Float64Array",
|
|
||||||
"Int8Array",
|
|
||||||
"Uint8Array",
|
|
||||||
"Uint8ClampedArray",
|
|
||||||
"Int16Array",
|
|
||||||
"Int32Array",
|
|
||||||
"Uint16Array",
|
|
||||||
"Uint32Array",
|
|
||||||
"BigInt64Array",
|
|
||||||
"BigUint64Array",
|
|
||||||
// Keyed collections
|
|
||||||
"Set",
|
|
||||||
"Map",
|
|
||||||
"WeakSet",
|
|
||||||
"WeakMap",
|
|
||||||
// Structured data
|
|
||||||
"ArrayBuffer",
|
|
||||||
"SharedArrayBuffer",
|
|
||||||
"Atomics",
|
|
||||||
"DataView",
|
|
||||||
"JSON",
|
|
||||||
// Control abstraction objects
|
|
||||||
"Promise",
|
|
||||||
"Generator",
|
|
||||||
"GeneratorFunction",
|
|
||||||
"AsyncFunction",
|
|
||||||
// Reflection
|
|
||||||
"Reflect",
|
|
||||||
"Proxy",
|
|
||||||
// Internationalization
|
|
||||||
"Intl",
|
|
||||||
// WebAssembly
|
|
||||||
"WebAssembly"
|
|
||||||
];
|
|
||||||
|
|
||||||
const ERROR_TYPES = [
|
|
||||||
"Error",
|
|
||||||
"EvalError",
|
|
||||||
"InternalError",
|
|
||||||
"RangeError",
|
|
||||||
"ReferenceError",
|
|
||||||
"SyntaxError",
|
|
||||||
"TypeError",
|
|
||||||
"URIError"
|
|
||||||
];
|
|
||||||
|
|
||||||
const BUILT_IN_GLOBALS = [
|
|
||||||
"setInterval",
|
|
||||||
"setTimeout",
|
|
||||||
"clearInterval",
|
|
||||||
"clearTimeout",
|
|
||||||
|
|
||||||
"require",
|
|
||||||
"exports",
|
|
||||||
|
|
||||||
"eval",
|
|
||||||
"isFinite",
|
|
||||||
"isNaN",
|
|
||||||
"parseFloat",
|
|
||||||
"parseInt",
|
|
||||||
"decodeURI",
|
|
||||||
"decodeURIComponent",
|
|
||||||
"encodeURI",
|
|
||||||
"encodeURIComponent",
|
|
||||||
"escape",
|
|
||||||
"unescape"
|
|
||||||
];
|
|
||||||
|
|
||||||
const BUILT_IN_VARIABLES = [
|
|
||||||
"arguments",
|
|
||||||
"this",
|
|
||||||
"super",
|
|
||||||
"console",
|
|
||||||
"window",
|
|
||||||
"document",
|
|
||||||
"localStorage",
|
|
||||||
"sessionStorage",
|
|
||||||
"module",
|
|
||||||
"global" // Node.js
|
|
||||||
];
|
|
||||||
|
|
||||||
const BUILT_INS = [].concat(
|
|
||||||
BUILT_IN_GLOBALS,
|
|
||||||
TYPES,
|
|
||||||
ERROR_TYPES
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: JavaScript
|
|
||||||
Description: JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.
|
|
||||||
Category: common, scripting, web
|
|
||||||
Website: https://developer.mozilla.org/en-US/docs/Web/JavaScript
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/** @type LanguageFn */
|
|
||||||
function javascript(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
/**
|
|
||||||
* Takes a string like "<Booger" and checks to see
|
|
||||||
* if we can find a matching "</Booger" later in the
|
|
||||||
* content.
|
|
||||||
* @param {RegExpMatchArray} match
|
|
||||||
* @param {{after:number}} param1
|
|
||||||
*/
|
|
||||||
const hasClosingTag = (match, { after }) => {
|
|
||||||
const tag = "</" + match[0].slice(1);
|
|
||||||
const pos = match.input.indexOf(tag, after);
|
|
||||||
return pos !== -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const IDENT_RE$1 = IDENT_RE;
|
|
||||||
const FRAGMENT = {
|
|
||||||
begin: '<>',
|
|
||||||
end: '</>'
|
|
||||||
};
|
|
||||||
// to avoid some special cases inside isTrulyOpeningTag
|
|
||||||
const XML_SELF_CLOSING = /<[A-Za-z0-9\\._:-]+\s*\/>/;
|
|
||||||
const XML_TAG = {
|
|
||||||
begin: /<[A-Za-z0-9\\._:-]+/,
|
|
||||||
end: /\/[A-Za-z0-9\\._:-]+>|\/>/,
|
|
||||||
/**
|
|
||||||
* @param {RegExpMatchArray} match
|
|
||||||
* @param {CallbackResponse} response
|
|
||||||
*/
|
|
||||||
isTrulyOpeningTag: (match, response) => {
|
|
||||||
const afterMatchIndex = match[0].length + match.index;
|
|
||||||
const nextChar = match.input[afterMatchIndex];
|
|
||||||
if (
|
|
||||||
// HTML should not include another raw `<` inside a tag
|
|
||||||
// nested type?
|
|
||||||
// `<Array<Array<number>>`, etc.
|
|
||||||
nextChar === "<" ||
|
|
||||||
// the , gives away that this is not HTML
|
|
||||||
// `<T, A extends keyof T, V>`
|
|
||||||
nextChar === ","
|
|
||||||
) {
|
|
||||||
response.ignoreMatch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `<something>`
|
|
||||||
// Quite possibly a tag, lets look for a matching closing tag...
|
|
||||||
if (nextChar === ">") {
|
|
||||||
// if we cannot find a matching closing tag, then we
|
|
||||||
// will ignore it
|
|
||||||
if (!hasClosingTag(match, { after: afterMatchIndex })) {
|
|
||||||
response.ignoreMatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// `<blah />` (self-closing)
|
|
||||||
// handled by simpleSelfClosing rule
|
|
||||||
|
|
||||||
let m;
|
|
||||||
const afterMatch = match.input.substring(afterMatchIndex);
|
|
||||||
|
|
||||||
// some more template typing stuff
|
|
||||||
// <T = any>(key?: string) => Modify<
|
|
||||||
if ((m = afterMatch.match(/^\s*=/))) {
|
|
||||||
response.ignoreMatch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `<From extends string>`
|
|
||||||
// technically this could be HTML, but it smells like a type
|
|
||||||
// NOTE: This is ugh, but added specifically for https://github.com/highlightjs/highlight.js/issues/3276
|
|
||||||
if ((m = afterMatch.match(/^\s+extends\s+/))) {
|
|
||||||
if (m.index === 0) {
|
|
||||||
response.ignoreMatch();
|
|
||||||
// eslint-disable-next-line no-useless-return
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const KEYWORDS$1 = {
|
|
||||||
$pattern: IDENT_RE,
|
|
||||||
keyword: KEYWORDS,
|
|
||||||
literal: LITERALS,
|
|
||||||
built_in: BUILT_INS,
|
|
||||||
"variable.language": BUILT_IN_VARIABLES
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://tc39.es/ecma262/#sec-literals-numeric-literals
|
|
||||||
const decimalDigits = '[0-9](_?[0-9])*';
|
|
||||||
const frac = `\\.(${decimalDigits})`;
|
|
||||||
// DecimalIntegerLiteral, including Annex B NonOctalDecimalIntegerLiteral
|
|
||||||
// https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
|
|
||||||
const decimalInteger = `0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`;
|
|
||||||
const NUMBER = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
// DecimalLiteral
|
|
||||||
{ begin: `(\\b(${decimalInteger})((${frac})|\\.)?|(${frac}))` +
|
|
||||||
`[eE][+-]?(${decimalDigits})\\b` },
|
|
||||||
{ begin: `\\b(${decimalInteger})\\b((${frac})\\b|\\.)?|(${frac})\\b` },
|
|
||||||
|
|
||||||
// DecimalBigIntegerLiteral
|
|
||||||
{ begin: `\\b(0|[1-9](_?[0-9])*)n\\b` },
|
|
||||||
|
|
||||||
// NonDecimalIntegerLiteral
|
|
||||||
{ begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b" },
|
|
||||||
{ begin: "\\b0[bB][0-1](_?[0-1])*n?\\b" },
|
|
||||||
{ begin: "\\b0[oO][0-7](_?[0-7])*n?\\b" },
|
|
||||||
|
|
||||||
// LegacyOctalIntegerLiteral (does not include underscore separators)
|
|
||||||
// https://tc39.es/ecma262/#sec-additional-syntax-numeric-literals
|
|
||||||
{ begin: "\\b0[0-7]+n?\\b" },
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const SUBST = {
|
|
||||||
className: 'subst',
|
|
||||||
begin: '\\$\\{',
|
|
||||||
end: '\\}',
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
contains: [] // defined later
|
|
||||||
};
|
|
||||||
const HTML_TEMPLATE = {
|
|
||||||
begin: '\.?html`',
|
|
||||||
end: '',
|
|
||||||
starts: {
|
|
||||||
end: '`',
|
|
||||||
returnEnd: false,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
SUBST
|
|
||||||
],
|
|
||||||
subLanguage: 'xml'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const CSS_TEMPLATE = {
|
|
||||||
begin: '\.?css`',
|
|
||||||
end: '',
|
|
||||||
starts: {
|
|
||||||
end: '`',
|
|
||||||
returnEnd: false,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
SUBST
|
|
||||||
],
|
|
||||||
subLanguage: 'css'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const GRAPHQL_TEMPLATE = {
|
|
||||||
begin: '\.?gql`',
|
|
||||||
end: '',
|
|
||||||
starts: {
|
|
||||||
end: '`',
|
|
||||||
returnEnd: false,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
SUBST
|
|
||||||
],
|
|
||||||
subLanguage: 'graphql'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const TEMPLATE_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: '`',
|
|
||||||
end: '`',
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
SUBST
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const JSDOC_COMMENT = hljs.COMMENT(
|
|
||||||
/\/\*\*(?!\/)/,
|
|
||||||
'\\*/',
|
|
||||||
{
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: '(?=@[A-Za-z]+)',
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'doctag',
|
|
||||||
begin: '@[A-Za-z]+'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'type',
|
|
||||||
begin: '\\{',
|
|
||||||
end: '\\}',
|
|
||||||
excludeEnd: true,
|
|
||||||
excludeBegin: true,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'variable',
|
|
||||||
begin: IDENT_RE$1 + '(?=\\s*(-)|$)',
|
|
||||||
endsParent: true,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
// eat spaces (not newlines) so we can find
|
|
||||||
// types or variables
|
|
||||||
{
|
|
||||||
begin: /(?=[^\n])\s/,
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const COMMENT = {
|
|
||||||
className: "comment",
|
|
||||||
variants: [
|
|
||||||
JSDOC_COMMENT,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
hljs.C_LINE_COMMENT_MODE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const SUBST_INTERNALS = [
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
HTML_TEMPLATE,
|
|
||||||
CSS_TEMPLATE,
|
|
||||||
GRAPHQL_TEMPLATE,
|
|
||||||
TEMPLATE_STRING,
|
|
||||||
// Skip numbers when they are part of a variable name
|
|
||||||
{ match: /\$\d+/ },
|
|
||||||
NUMBER,
|
|
||||||
// This is intentional:
|
|
||||||
// See https://github.com/highlightjs/highlight.js/issues/3288
|
|
||||||
// hljs.REGEXP_MODE
|
|
||||||
];
|
|
||||||
SUBST.contains = SUBST_INTERNALS
|
|
||||||
.concat({
|
|
||||||
// we need to pair up {} inside our subst to prevent
|
|
||||||
// it from ending too early by matching another }
|
|
||||||
begin: /\{/,
|
|
||||||
end: /\}/,
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
contains: [
|
|
||||||
"self"
|
|
||||||
].concat(SUBST_INTERNALS)
|
|
||||||
});
|
|
||||||
const SUBST_AND_COMMENTS = [].concat(COMMENT, SUBST.contains);
|
|
||||||
const PARAMS_CONTAINS = SUBST_AND_COMMENTS.concat([
|
|
||||||
// eat recursive parens in sub expressions
|
|
||||||
{
|
|
||||||
begin: /(\s*)\(/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
contains: ["self"].concat(SUBST_AND_COMMENTS)
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
const PARAMS = {
|
|
||||||
className: 'params',
|
|
||||||
// convert this to negative lookbehind in v12
|
|
||||||
begin: /(\s*)\(/, // to match the parms with
|
|
||||||
end: /\)/,
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
contains: PARAMS_CONTAINS
|
|
||||||
};
|
|
||||||
|
|
||||||
// ES6 classes
|
|
||||||
const CLASS_OR_EXTENDS = {
|
|
||||||
variants: [
|
|
||||||
// class Car extends vehicle
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/class/,
|
|
||||||
/\s+/,
|
|
||||||
IDENT_RE$1,
|
|
||||||
/\s+/,
|
|
||||||
/extends/,
|
|
||||||
/\s+/,
|
|
||||||
regex.concat(IDENT_RE$1, "(", regex.concat(/\./, IDENT_RE$1), ")*")
|
|
||||||
],
|
|
||||||
scope: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.class",
|
|
||||||
5: "keyword",
|
|
||||||
7: "title.class.inherited"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// class Car
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/class/,
|
|
||||||
/\s+/,
|
|
||||||
IDENT_RE$1
|
|
||||||
],
|
|
||||||
scope: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.class"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const CLASS_REFERENCE = {
|
|
||||||
relevance: 0,
|
|
||||||
match:
|
|
||||||
regex.either(
|
|
||||||
// Hard coded exceptions
|
|
||||||
/\bJSON/,
|
|
||||||
// Float32Array, OutT
|
|
||||||
/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,
|
|
||||||
// CSSFactory, CSSFactoryT
|
|
||||||
/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,
|
|
||||||
// FPs, FPsT
|
|
||||||
/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/,
|
|
||||||
// P
|
|
||||||
// single letters are not highlighted
|
|
||||||
// BLAH
|
|
||||||
// this will be flagged as a UPPER_CASE_CONSTANT instead
|
|
||||||
),
|
|
||||||
className: "title.class",
|
|
||||||
keywords: {
|
|
||||||
_: [
|
|
||||||
// se we still get relevance credit for JS library classes
|
|
||||||
...TYPES,
|
|
||||||
...ERROR_TYPES
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const USE_STRICT = {
|
|
||||||
label: "use_strict",
|
|
||||||
className: 'meta',
|
|
||||||
relevance: 10,
|
|
||||||
begin: /^\s*['"]use (strict|asm)['"]/
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNCTION_DEFINITION = {
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/function/,
|
|
||||||
/\s+/,
|
|
||||||
IDENT_RE$1,
|
|
||||||
/(?=\s*\()/
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// anonymous function
|
|
||||||
{
|
|
||||||
match: [
|
|
||||||
/function/,
|
|
||||||
/\s*(?=\()/
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.function"
|
|
||||||
},
|
|
||||||
label: "func.def",
|
|
||||||
contains: [ PARAMS ],
|
|
||||||
illegal: /%/
|
|
||||||
};
|
|
||||||
|
|
||||||
const UPPER_CASE_CONSTANT = {
|
|
||||||
relevance: 0,
|
|
||||||
match: /\b[A-Z][A-Z_0-9]+\b/,
|
|
||||||
className: "variable.constant"
|
|
||||||
};
|
|
||||||
|
|
||||||
function noneOf(list) {
|
|
||||||
return regex.concat("(?!", list.join("|"), ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
const FUNCTION_CALL = {
|
|
||||||
match: regex.concat(
|
|
||||||
/\b/,
|
|
||||||
noneOf([
|
|
||||||
...BUILT_IN_GLOBALS,
|
|
||||||
"super",
|
|
||||||
"import"
|
|
||||||
].map(x => `${x}\\s*\\(`)),
|
|
||||||
IDENT_RE$1, regex.lookahead(/\s*\(/)),
|
|
||||||
className: "title.function",
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const PROPERTY_ACCESS = {
|
|
||||||
begin: regex.concat(/\./, regex.lookahead(
|
|
||||||
regex.concat(IDENT_RE$1, /(?![0-9A-Za-z$_(])/)
|
|
||||||
)),
|
|
||||||
end: IDENT_RE$1,
|
|
||||||
excludeBegin: true,
|
|
||||||
keywords: "prototype",
|
|
||||||
className: "property",
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const GETTER_OR_SETTER = {
|
|
||||||
match: [
|
|
||||||
/get|set/,
|
|
||||||
/\s+/,
|
|
||||||
IDENT_RE$1,
|
|
||||||
/(?=\()/
|
|
||||||
],
|
|
||||||
className: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.function"
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
{ // eat to avoid empty params
|
|
||||||
begin: /\(\)/
|
|
||||||
},
|
|
||||||
PARAMS
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const FUNC_LEAD_IN_RE = '(\\(' +
|
|
||||||
'[^()]*(\\(' +
|
|
||||||
'[^()]*(\\(' +
|
|
||||||
'[^()]*' +
|
|
||||||
'\\)[^()]*)*' +
|
|
||||||
'\\)[^()]*)*' +
|
|
||||||
'\\)|' + hljs.UNDERSCORE_IDENT_RE + ')\\s*=>';
|
|
||||||
|
|
||||||
const FUNCTION_VARIABLE = {
|
|
||||||
match: [
|
|
||||||
/const|var|let/, /\s+/,
|
|
||||||
IDENT_RE$1, /\s*/,
|
|
||||||
/=\s*/,
|
|
||||||
/(async\s*)?/, // async is optional
|
|
||||||
regex.lookahead(FUNC_LEAD_IN_RE)
|
|
||||||
],
|
|
||||||
keywords: "async",
|
|
||||||
className: {
|
|
||||||
1: "keyword",
|
|
||||||
3: "title.function"
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
PARAMS
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'JavaScript',
|
|
||||||
aliases: ['js', 'jsx', 'mjs', 'cjs'],
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
// this will be extended by TypeScript
|
|
||||||
exports: { PARAMS_CONTAINS, CLASS_REFERENCE },
|
|
||||||
illegal: /#(?![$_A-z])/,
|
|
||||||
contains: [
|
|
||||||
hljs.SHEBANG({
|
|
||||||
label: "shebang",
|
|
||||||
binary: "node",
|
|
||||||
relevance: 5
|
|
||||||
}),
|
|
||||||
USE_STRICT,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
HTML_TEMPLATE,
|
|
||||||
CSS_TEMPLATE,
|
|
||||||
GRAPHQL_TEMPLATE,
|
|
||||||
TEMPLATE_STRING,
|
|
||||||
COMMENT,
|
|
||||||
// Skip numbers when they are part of a variable name
|
|
||||||
{ match: /\$\d+/ },
|
|
||||||
NUMBER,
|
|
||||||
CLASS_REFERENCE,
|
|
||||||
{
|
|
||||||
scope: 'attr',
|
|
||||||
match: IDENT_RE$1 + regex.lookahead(':'),
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
FUNCTION_VARIABLE,
|
|
||||||
{ // "value" container
|
|
||||||
begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
|
|
||||||
keywords: 'return throw case',
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
COMMENT,
|
|
||||||
hljs.REGEXP_MODE,
|
|
||||||
{
|
|
||||||
className: 'function',
|
|
||||||
// we have to count the parens to make sure we actually have the
|
|
||||||
// correct bounding ( ) before the =>. There could be any number of
|
|
||||||
// sub-expressions inside also surrounded by parens.
|
|
||||||
begin: FUNC_LEAD_IN_RE,
|
|
||||||
returnBegin: true,
|
|
||||||
end: '\\s*=>',
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: hljs.UNDERSCORE_IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: null,
|
|
||||||
begin: /\(\s*\)/,
|
|
||||||
skip: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /(\s*)\(/,
|
|
||||||
end: /\)/,
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS$1,
|
|
||||||
contains: PARAMS_CONTAINS
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ // could be a comma delimited list of params to a function call
|
|
||||||
begin: /,/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /\s+/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{ // JSX
|
|
||||||
variants: [
|
|
||||||
{ begin: FRAGMENT.begin, end: FRAGMENT.end },
|
|
||||||
{ match: XML_SELF_CLOSING },
|
|
||||||
{
|
|
||||||
begin: XML_TAG.begin,
|
|
||||||
// we carefully check the opening tag to see if it truly
|
|
||||||
// is a tag and not a false positive
|
|
||||||
'on:begin': XML_TAG.isTrulyOpeningTag,
|
|
||||||
end: XML_TAG.end
|
|
||||||
}
|
|
||||||
],
|
|
||||||
subLanguage: 'xml',
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: XML_TAG.begin,
|
|
||||||
end: XML_TAG.end,
|
|
||||||
skip: true,
|
|
||||||
contains: ['self']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
FUNCTION_DEFINITION,
|
|
||||||
{
|
|
||||||
// prevent this from getting swallowed up by function
|
|
||||||
// since they appear "function like"
|
|
||||||
beginKeywords: "while if switch catch for"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// we have to count the parens to make sure we actually have the correct
|
|
||||||
// bounding ( ). There could be any number of sub-expressions inside
|
|
||||||
// also surrounded by parens.
|
|
||||||
begin: '\\b(?!function)' + hljs.UNDERSCORE_IDENT_RE +
|
|
||||||
'\\(' + // first parens
|
|
||||||
'[^()]*(\\(' +
|
|
||||||
'[^()]*(\\(' +
|
|
||||||
'[^()]*' +
|
|
||||||
'\\)[^()]*)*' +
|
|
||||||
'\\)[^()]*)*' +
|
|
||||||
'\\)\\s*\\{', // end parens
|
|
||||||
returnBegin:true,
|
|
||||||
label: "func.def",
|
|
||||||
contains: [
|
|
||||||
PARAMS,
|
|
||||||
hljs.inherit(hljs.TITLE_MODE, { begin: IDENT_RE$1, className: "title.function" })
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// catch ... so it won't trigger the property rule below
|
|
||||||
{
|
|
||||||
match: /\.\.\./,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
PROPERTY_ACCESS,
|
|
||||||
// hack: prevents detection of keywords in some circumstances
|
|
||||||
// .keyword()
|
|
||||||
// $keyword = x
|
|
||||||
{
|
|
||||||
match: '\\$' + IDENT_RE$1,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: [ /\bconstructor(?=\s*\()/ ],
|
|
||||||
className: { 1: "title.function" },
|
|
||||||
contains: [ PARAMS ]
|
|
||||||
},
|
|
||||||
FUNCTION_CALL,
|
|
||||||
UPPER_CASE_CONSTANT,
|
|
||||||
CLASS_OR_EXTENDS,
|
|
||||||
GETTER_OR_SETTER,
|
|
||||||
{
|
|
||||||
match: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return javascript;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/*! `javascript` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict"
|
|
||||||
;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends","using"],a=["true","false","null","undefined","NaN","Infinity"],t=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],s=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],c=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],i=[].concat(r,t,s)
|
|
||||||
;return o=>{const l=o.regex,d=e,b={begin:/<[A-Za-z0-9\\._:-]+/,
|
|
||||||
end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{
|
|
||||||
const a=e[0].length+e.index,t=e.input[a]
|
|
||||||
;if("<"===t||","===t)return void n.ignoreMatch();let s
|
|
||||||
;">"===t&&(((e,{after:n})=>{const a="</"+e[0].slice(1)
|
|
||||||
;return-1!==e.input.indexOf(a,n)})(e,{after:a})||n.ignoreMatch())
|
|
||||||
;const r=e.input.substring(a)
|
|
||||||
;((s=r.match(/^\s*=/))||(s=r.match(/^\s+extends\s+/))&&0===s.index)&&n.ignoreMatch()
|
|
||||||
}},g={$pattern:e,keyword:n,literal:a,built_in:i,"variable.language":c
|
|
||||||
},u="[0-9](_?[0-9])*",m=`\\.(${u})`,E="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",A={
|
|
||||||
className:"number",variants:[{
|
|
||||||
begin:`(\\b(${E})((${m})|\\.)?|(${m}))[eE][+-]?(${u})\\b`},{
|
|
||||||
begin:`\\b(${E})\\b((${m})\\b|\\.)?|(${m})\\b`},{
|
|
||||||
begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{
|
|
||||||
begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{
|
|
||||||
begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
|
|
||||||
begin:"\\b0[0-7]+n?\\b"}],relevance:0},y={className:"subst",begin:"\\$\\{",
|
|
||||||
end:"\\}",keywords:g,contains:[]},h={begin:".?html`",end:"",starts:{end:"`",
|
|
||||||
returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"xml"}},_={
|
|
||||||
begin:".?css`",end:"",starts:{end:"`",returnEnd:!1,
|
|
||||||
contains:[o.BACKSLASH_ESCAPE,y],subLanguage:"css"}},N={begin:".?gql`",end:"",
|
|
||||||
starts:{end:"`",returnEnd:!1,contains:[o.BACKSLASH_ESCAPE,y],
|
|
||||||
subLanguage:"graphql"}},f={className:"string",begin:"`",end:"`",
|
|
||||||
contains:[o.BACKSLASH_ESCAPE,y]},p={className:"comment",
|
|
||||||
variants:[o.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
|
|
||||||
begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",
|
|
||||||
begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,
|
|
||||||
excludeBegin:!0,relevance:0},{className:"variable",begin:d+"(?=\\s*(-)|$)",
|
|
||||||
endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]
|
|
||||||
}),o.C_BLOCK_COMMENT_MODE,o.C_LINE_COMMENT_MODE]
|
|
||||||
},v=[o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,_,N,f,{match:/\$\d+/},A]
|
|
||||||
;y.contains=v.concat({begin:/\{/,end:/\}/,keywords:g,contains:["self"].concat(v)
|
|
||||||
});const S=[].concat(p,y.contains),w=S.concat([{begin:/(\s*)\(/,end:/\)/,
|
|
||||||
keywords:g,contains:["self"].concat(S)}]),R={className:"params",begin:/(\s*)\(/,
|
|
||||||
end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w},O={variants:[{
|
|
||||||
match:[/class/,/\s+/,d,/\s+/,/extends/,/\s+/,l.concat(d,"(",l.concat(/\./,d),")*")],
|
|
||||||
scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{
|
|
||||||
match:[/class/,/\s+/,d],scope:{1:"keyword",3:"title.class"}}]},k={relevance:0,
|
|
||||||
match:l.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),
|
|
||||||
className:"title.class",keywords:{_:[...t,...s]}},I={variants:[{
|
|
||||||
match:[/function/,/\s+/,d,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],
|
|
||||||
className:{1:"keyword",3:"title.function"},label:"func.def",contains:[R],
|
|
||||||
illegal:/%/},x={
|
|
||||||
match:l.concat(/\b/,(T=[...r,"super","import"].map((e=>e+"\\s*\\(")),
|
|
||||||
l.concat("(?!",T.join("|"),")")),d,l.lookahead(/\s*\(/)),
|
|
||||||
className:"title.function",relevance:0};var T;const C={
|
|
||||||
begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d,
|
|
||||||
excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},M={
|
|
||||||
match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"},
|
|
||||||
contains:[{begin:/\(\)/},R]
|
|
||||||
},B="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+o.UNDERSCORE_IDENT_RE+")\\s*=>",$={
|
|
||||||
match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,/(async\s*)?/,l.lookahead(B)],
|
|
||||||
keywords:"async",className:{1:"keyword",3:"title.function"},contains:[R]}
|
|
||||||
;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:g,exports:{
|
|
||||||
PARAMS_CONTAINS:w,CLASS_REFERENCE:k},illegal:/#(?![$_A-z])/,
|
|
||||||
contains:[o.SHEBANG({label:"shebang",binary:"node",relevance:5}),{
|
|
||||||
label:"use_strict",className:"meta",relevance:10,
|
|
||||||
begin:/^\s*['"]use (strict|asm)['"]/
|
|
||||||
},o.APOS_STRING_MODE,o.QUOTE_STRING_MODE,h,_,N,f,p,{match:/\$\d+/},A,k,{
|
|
||||||
scope:"attr",match:d+l.lookahead(":"),relevance:0},$,{
|
|
||||||
begin:"("+o.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
|
|
||||||
keywords:"return throw case",relevance:0,contains:[p,o.REGEXP_MODE,{
|
|
||||||
className:"function",begin:B,returnBegin:!0,end:"\\s*=>",contains:[{
|
|
||||||
className:"params",variants:[{begin:o.UNDERSCORE_IDENT_RE,relevance:0},{
|
|
||||||
className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,
|
|
||||||
excludeBegin:!0,excludeEnd:!0,keywords:g,contains:w}]}]},{begin:/,/,relevance:0
|
|
||||||
},{match:/\s+/,relevance:0},{variants:[{begin:"<>",end:"</>"},{
|
|
||||||
match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:b.begin,
|
|
||||||
"on:begin":b.isTrulyOpeningTag,end:b.end}],subLanguage:"xml",contains:[{
|
|
||||||
begin:b.begin,end:b.end,skip:!0,contains:["self"]}]}]},I,{
|
|
||||||
beginKeywords:"while if switch catch for"},{
|
|
||||||
begin:"\\b(?!function)"+o.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
|
|
||||||
returnBegin:!0,label:"func.def",contains:[R,o.inherit(o.TITLE_MODE,{begin:d,
|
|
||||||
className:"title.function"})]},{match:/\.\.\./,relevance:0},C,{match:"\\$"+d,
|
|
||||||
relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},
|
|
||||||
contains:[R]},x,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,
|
|
||||||
className:"variable.constant"},O,M,{match:/\$[(.]/}]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
/*! `json` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: JSON
|
|
||||||
Description: JSON (JavaScript Object Notation) is a lightweight data-interchange format.
|
|
||||||
Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
|
|
||||||
Website: http://www.json.org
|
|
||||||
Category: common, protocols, web
|
|
||||||
*/
|
|
||||||
|
|
||||||
function json(hljs) {
|
|
||||||
const ATTRIBUTE = {
|
|
||||||
className: 'attr',
|
|
||||||
begin: /"(\\.|[^\\"\r\n])*"(?=\s*:)/,
|
|
||||||
relevance: 1.01
|
|
||||||
};
|
|
||||||
const PUNCTUATION = {
|
|
||||||
match: /[{}[\],:]/,
|
|
||||||
className: "punctuation",
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
const LITERALS = [
|
|
||||||
"true",
|
|
||||||
"false",
|
|
||||||
"null"
|
|
||||||
];
|
|
||||||
// NOTE: normally we would rely on `keywords` for this but using a mode here allows us
|
|
||||||
// - to use the very tight `illegal: \S` rule later to flag any other character
|
|
||||||
// - as illegal indicating that despite looking like JSON we do not truly have
|
|
||||||
// - JSON and thus improve false-positively greatly since JSON will try and claim
|
|
||||||
// - all sorts of JSON looking stuff
|
|
||||||
const LITERALS_MODE = {
|
|
||||||
scope: "literal",
|
|
||||||
beginKeywords: LITERALS.join(" "),
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'JSON',
|
|
||||||
aliases: ['jsonc'],
|
|
||||||
keywords:{
|
|
||||||
literal: LITERALS,
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
ATTRIBUTE,
|
|
||||||
PUNCTUATION,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
LITERALS_MODE,
|
|
||||||
hljs.C_NUMBER_MODE,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
],
|
|
||||||
illegal: '\\S'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/*! `json` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{
|
|
||||||
const a=["true","false","null"],r={scope:"literal",beginKeywords:a.join(" ")}
|
|
||||||
;return{name:"JSON",aliases:["jsonc"],keywords:{literal:a},contains:[{
|
|
||||||
className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{
|
|
||||||
match:/[{}[\],:]/,className:"punctuation",relevance:0
|
|
||||||
},e.QUOTE_STRING_MODE,r,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],
|
|
||||||
illegal:"\\S"}}})();export default hljsGrammar;
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
/*! `kotlin` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// https://docs.oracle.com/javase/specs/jls/se15/html/jls-3.html#jls-3.10
|
|
||||||
var decimalDigits = '[0-9](_*[0-9])*';
|
|
||||||
var frac = `\\.(${decimalDigits})`;
|
|
||||||
var hexDigits = '[0-9a-fA-F](_*[0-9a-fA-F])*';
|
|
||||||
var NUMERIC = {
|
|
||||||
className: 'number',
|
|
||||||
variants: [
|
|
||||||
// DecimalFloatingPointLiteral
|
|
||||||
// including ExponentPart
|
|
||||||
{ begin: `(\\b(${decimalDigits})((${frac})|\\.)?|(${frac}))` +
|
|
||||||
`[eE][+-]?(${decimalDigits})[fFdD]?\\b` },
|
|
||||||
// excluding ExponentPart
|
|
||||||
{ begin: `\\b(${decimalDigits})((${frac})[fFdD]?\\b|\\.([fFdD]\\b)?)` },
|
|
||||||
{ begin: `(${frac})[fFdD]?\\b` },
|
|
||||||
{ begin: `\\b(${decimalDigits})[fFdD]\\b` },
|
|
||||||
|
|
||||||
// HexadecimalFloatingPointLiteral
|
|
||||||
{ begin: `\\b0[xX]((${hexDigits})\\.?|(${hexDigits})?\\.(${hexDigits}))` +
|
|
||||||
`[pP][+-]?(${decimalDigits})[fFdD]?\\b` },
|
|
||||||
|
|
||||||
// DecimalIntegerLiteral
|
|
||||||
{ begin: '\\b(0|[1-9](_*[0-9])*)[lL]?\\b' },
|
|
||||||
|
|
||||||
// HexIntegerLiteral
|
|
||||||
{ begin: `\\b0[xX](${hexDigits})[lL]?\\b` },
|
|
||||||
|
|
||||||
// OctalIntegerLiteral
|
|
||||||
{ begin: '\\b0(_*[0-7])*[lL]?\\b' },
|
|
||||||
|
|
||||||
// BinaryIntegerLiteral
|
|
||||||
{ begin: '\\b0[bB][01](_*[01])*[lL]?\\b' },
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Kotlin
|
|
||||||
Description: Kotlin is an OSS statically typed programming language that targets the JVM, Android, JavaScript and Native.
|
|
||||||
Author: Sergey Mashkov <cy6erGn0m@gmail.com>
|
|
||||||
Website: https://kotlinlang.org
|
|
||||||
Category: common
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
function kotlin(hljs) {
|
|
||||||
const KEYWORDS = {
|
|
||||||
keyword:
|
|
||||||
'abstract as val var vararg get set class object open private protected public noinline '
|
|
||||||
+ 'crossinline dynamic final enum if else do while for when throw try catch finally '
|
|
||||||
+ 'import package is in fun override companion reified inline lateinit init '
|
|
||||||
+ 'interface annotation data sealed internal infix operator out by constructor super '
|
|
||||||
+ 'tailrec where const inner suspend typealias external expect actual',
|
|
||||||
built_in:
|
|
||||||
'Byte Short Char Int Long Boolean Float Double Void Unit Nothing',
|
|
||||||
literal:
|
|
||||||
'true false null'
|
|
||||||
};
|
|
||||||
const KEYWORDS_WITH_LABEL = {
|
|
||||||
className: 'keyword',
|
|
||||||
begin: /\b(break|continue|return|this)\b/,
|
|
||||||
starts: { contains: [
|
|
||||||
{
|
|
||||||
className: 'symbol',
|
|
||||||
begin: /@\w+/
|
|
||||||
}
|
|
||||||
] }
|
|
||||||
};
|
|
||||||
const LABEL = {
|
|
||||||
className: 'symbol',
|
|
||||||
begin: hljs.UNDERSCORE_IDENT_RE + '@'
|
|
||||||
};
|
|
||||||
|
|
||||||
// for string templates
|
|
||||||
const SUBST = {
|
|
||||||
className: 'subst',
|
|
||||||
begin: /\$\{/,
|
|
||||||
end: /\}/,
|
|
||||||
contains: [ hljs.C_NUMBER_MODE ]
|
|
||||||
};
|
|
||||||
const VARIABLE = {
|
|
||||||
className: 'variable',
|
|
||||||
begin: '\\$' + hljs.UNDERSCORE_IDENT_RE
|
|
||||||
};
|
|
||||||
const STRING = {
|
|
||||||
className: 'string',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '"""',
|
|
||||||
end: '"""(?=[^"])',
|
|
||||||
contains: [
|
|
||||||
VARIABLE,
|
|
||||||
SUBST
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// Can't use built-in modes easily, as we want to use STRING in the meta
|
|
||||||
// context as 'meta-string' and there's no syntax to remove explicitly set
|
|
||||||
// classNames in built-in modes.
|
|
||||||
{
|
|
||||||
begin: '\'',
|
|
||||||
end: '\'',
|
|
||||||
illegal: /\n/,
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '"',
|
|
||||||
end: '"',
|
|
||||||
illegal: /\n/,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
VARIABLE,
|
|
||||||
SUBST
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
SUBST.contains.push(STRING);
|
|
||||||
|
|
||||||
const ANNOTATION_USE_SITE = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: '@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*' + hljs.UNDERSCORE_IDENT_RE + ')?'
|
|
||||||
};
|
|
||||||
const ANNOTATION = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: '@' + hljs.UNDERSCORE_IDENT_RE,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
contains: [
|
|
||||||
hljs.inherit(STRING, { className: 'string' }),
|
|
||||||
"self"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://kotlinlang.org/docs/reference/whatsnew11.html#underscores-in-numeric-literals
|
|
||||||
// According to the doc above, the number mode of kotlin is the same as java 8,
|
|
||||||
// so the code below is copied from java.js
|
|
||||||
const KOTLIN_NUMBER_MODE = NUMERIC;
|
|
||||||
const KOTLIN_NESTED_COMMENT = hljs.COMMENT(
|
|
||||||
'/\\*', '\\*/',
|
|
||||||
{ contains: [ hljs.C_BLOCK_COMMENT_MODE ] }
|
|
||||||
);
|
|
||||||
const KOTLIN_PAREN_TYPE = { variants: [
|
|
||||||
{
|
|
||||||
className: 'type',
|
|
||||||
begin: hljs.UNDERSCORE_IDENT_RE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
contains: [] // defined later
|
|
||||||
}
|
|
||||||
] };
|
|
||||||
const KOTLIN_PAREN_TYPE2 = KOTLIN_PAREN_TYPE;
|
|
||||||
KOTLIN_PAREN_TYPE2.variants[1].contains = [ KOTLIN_PAREN_TYPE ];
|
|
||||||
KOTLIN_PAREN_TYPE.variants[1].contains = [ KOTLIN_PAREN_TYPE2 ];
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Kotlin',
|
|
||||||
aliases: [
|
|
||||||
'kt',
|
|
||||||
'kts'
|
|
||||||
],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
contains: [
|
|
||||||
hljs.COMMENT(
|
|
||||||
'/\\*\\*',
|
|
||||||
'\\*/',
|
|
||||||
{
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'doctag',
|
|
||||||
begin: '@[A-Za-z]+'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
KOTLIN_NESTED_COMMENT,
|
|
||||||
KEYWORDS_WITH_LABEL,
|
|
||||||
LABEL,
|
|
||||||
ANNOTATION_USE_SITE,
|
|
||||||
ANNOTATION,
|
|
||||||
{
|
|
||||||
className: 'function',
|
|
||||||
beginKeywords: 'fun',
|
|
||||||
end: '[(]|$',
|
|
||||||
returnBegin: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 5,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: hljs.UNDERSCORE_IDENT_RE + '\\s*\\(',
|
|
||||||
returnBegin: true,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [ hljs.UNDERSCORE_TITLE_MODE ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'type',
|
|
||||||
begin: /</,
|
|
||||||
end: />/,
|
|
||||||
keywords: 'reified',
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: /\(/,
|
|
||||||
end: /\)/,
|
|
||||||
endsParent: true,
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
relevance: 0,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /:/,
|
|
||||||
end: /[=,\/]/,
|
|
||||||
endsWithParent: true,
|
|
||||||
contains: [
|
|
||||||
KOTLIN_PAREN_TYPE,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
KOTLIN_NESTED_COMMENT
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
KOTLIN_NESTED_COMMENT,
|
|
||||||
ANNOTATION_USE_SITE,
|
|
||||||
ANNOTATION,
|
|
||||||
STRING,
|
|
||||||
hljs.C_NUMBER_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
KOTLIN_NESTED_COMMENT
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: [
|
|
||||||
/class|interface|trait/,
|
|
||||||
/\s+/,
|
|
||||||
hljs.UNDERSCORE_IDENT_RE
|
|
||||||
],
|
|
||||||
beginScope: {
|
|
||||||
3: "title.class"
|
|
||||||
},
|
|
||||||
keywords: 'class interface trait',
|
|
||||||
end: /[:\{(]|$/,
|
|
||||||
excludeEnd: true,
|
|
||||||
illegal: 'extends implements',
|
|
||||||
contains: [
|
|
||||||
{ beginKeywords: 'public protected internal private constructor' },
|
|
||||||
hljs.UNDERSCORE_TITLE_MODE,
|
|
||||||
{
|
|
||||||
className: 'type',
|
|
||||||
begin: /</,
|
|
||||||
end: />/,
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'type',
|
|
||||||
begin: /[,:]\s*/,
|
|
||||||
end: /[<\(,){\s]|$/,
|
|
||||||
excludeBegin: true,
|
|
||||||
returnEnd: true
|
|
||||||
},
|
|
||||||
ANNOTATION_USE_SITE,
|
|
||||||
ANNOTATION
|
|
||||||
]
|
|
||||||
},
|
|
||||||
STRING,
|
|
||||||
{
|
|
||||||
className: 'meta',
|
|
||||||
begin: "^#!/usr/bin/env",
|
|
||||||
end: '$',
|
|
||||||
illegal: '\n'
|
|
||||||
},
|
|
||||||
KOTLIN_NUMBER_MODE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return kotlin;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*! `kotlin` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict"
|
|
||||||
;var e="[0-9](_*[0-9])*",n=`\\.(${e})`,a="[0-9a-fA-F](_*[0-9a-fA-F])*",i={
|
|
||||||
className:"number",variants:[{
|
|
||||||
begin:`(\\b(${e})((${n})|\\.)?|(${n}))[eE][+-]?(${e})[fFdD]?\\b`},{
|
|
||||||
begin:`\\b(${e})((${n})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${n})[fFdD]?\\b`
|
|
||||||
},{begin:`\\b(${e})[fFdD]\\b`},{
|
|
||||||
begin:`\\b0[xX]((${a})\\.?|(${a})?\\.(${a}))[pP][+-]?(${e})[fFdD]?\\b`},{
|
|
||||||
begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${a})[lL]?\\b`},{
|
|
||||||
begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],
|
|
||||||
relevance:0};return e=>{const n={
|
|
||||||
keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual",
|
|
||||||
built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",
|
|
||||||
literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"
|
|
||||||
},s={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},t={
|
|
||||||
className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string",
|
|
||||||
variants:[{begin:'"""',end:'"""(?=[^"])',contains:[t,s]},{begin:"'",end:"'",
|
|
||||||
illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,
|
|
||||||
contains:[e.BACKSLASH_ESCAPE,t,s]}]};s.contains.push(r);const l={
|
|
||||||
className:"meta",
|
|
||||||
begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"
|
|
||||||
},c={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,
|
|
||||||
end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}]
|
|
||||||
},o=i,b=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),E={
|
|
||||||
variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,
|
|
||||||
contains:[]}]},d=E;return d.variants[1].contains=[E],E.variants[1].contains=[d],
|
|
||||||
{name:"Kotlin",aliases:["kt","kts"],keywords:n,
|
|
||||||
contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",
|
|
||||||
begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,b,{className:"keyword",
|
|
||||||
begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",
|
|
||||||
begin:/@\w+/}]}},a,l,c,{className:"function",beginKeywords:"fun",end:"[(]|$",
|
|
||||||
returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{
|
|
||||||
begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,
|
|
||||||
contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin:/</,end:/>/,
|
|
||||||
keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,
|
|
||||||
endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,
|
|
||||||
endsWithParent:!0,contains:[E,e.C_LINE_COMMENT_MODE,b],relevance:0
|
|
||||||
},e.C_LINE_COMMENT_MODE,b,l,c,r,e.C_NUMBER_MODE]},b]},{
|
|
||||||
begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{
|
|
||||||
3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,
|
|
||||||
illegal:"extends implements",contains:[{
|
|
||||||
beginKeywords:"public protected internal private constructor"
|
|
||||||
},e.UNDERSCORE_TITLE_MODE,{className:"type",begin:/</,end:/>/,excludeBegin:!0,
|
|
||||||
excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/,
|
|
||||||
excludeBegin:!0,returnEnd:!0},l,c]},r,{className:"meta",begin:"^#!/usr/bin/env",
|
|
||||||
end:"$",illegal:"\n"},o]}}})();export default hljsGrammar;
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,89 +0,0 @@
|
|||||||
/*! `lua` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Lua
|
|
||||||
Description: Lua is a powerful, efficient, lightweight, embeddable scripting language.
|
|
||||||
Author: Andrew Fedorov <dmmdrs@mail.ru>
|
|
||||||
Category: common, gaming, scripting
|
|
||||||
Website: https://www.lua.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
function lua(hljs) {
|
|
||||||
const OPENING_LONG_BRACKET = '\\[=*\\[';
|
|
||||||
const CLOSING_LONG_BRACKET = '\\]=*\\]';
|
|
||||||
const LONG_BRACKETS = {
|
|
||||||
begin: OPENING_LONG_BRACKET,
|
|
||||||
end: CLOSING_LONG_BRACKET,
|
|
||||||
contains: [ 'self' ]
|
|
||||||
};
|
|
||||||
const COMMENTS = [
|
|
||||||
hljs.COMMENT('--(?!' + OPENING_LONG_BRACKET + ')', '$'),
|
|
||||||
hljs.COMMENT(
|
|
||||||
'--' + OPENING_LONG_BRACKET,
|
|
||||||
CLOSING_LONG_BRACKET,
|
|
||||||
{
|
|
||||||
contains: [ LONG_BRACKETS ],
|
|
||||||
relevance: 10
|
|
||||||
}
|
|
||||||
)
|
|
||||||
];
|
|
||||||
return {
|
|
||||||
name: 'Lua',
|
|
||||||
aliases: ['pluto'],
|
|
||||||
keywords: {
|
|
||||||
$pattern: hljs.UNDERSCORE_IDENT_RE,
|
|
||||||
literal: "true false nil",
|
|
||||||
keyword: "and break do else elseif end for goto if in local not or repeat return then until while",
|
|
||||||
built_in:
|
|
||||||
// Metatags and globals:
|
|
||||||
'_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len '
|
|
||||||
+ '__gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert '
|
|
||||||
// Standard methods and properties:
|
|
||||||
+ 'collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring '
|
|
||||||
+ 'module next pairs pcall print rawequal rawget rawset require select setfenv '
|
|
||||||
+ 'setmetatable tonumber tostring type unpack xpcall arg self '
|
|
||||||
// Library methods and properties (one line per library):
|
|
||||||
+ 'coroutine resume yield status wrap create running debug getupvalue '
|
|
||||||
+ 'debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv '
|
|
||||||
+ 'io lines write close flush open output type read stderr stdin input stdout popen tmpfile '
|
|
||||||
+ 'math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan '
|
|
||||||
+ 'os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall '
|
|
||||||
+ 'string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower '
|
|
||||||
+ 'table setn insert getn foreachi maxn foreach concat sort remove'
|
|
||||||
},
|
|
||||||
contains: COMMENTS.concat([
|
|
||||||
{
|
|
||||||
className: 'function',
|
|
||||||
beginKeywords: 'function',
|
|
||||||
end: '\\)',
|
|
||||||
contains: [
|
|
||||||
hljs.inherit(hljs.TITLE_MODE, { begin: '([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*' }),
|
|
||||||
{
|
|
||||||
className: 'params',
|
|
||||||
begin: '\\(',
|
|
||||||
endsWithParent: true,
|
|
||||||
contains: COMMENTS
|
|
||||||
}
|
|
||||||
].concat(COMMENTS)
|
|
||||||
},
|
|
||||||
hljs.C_NUMBER_MODE,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
begin: OPENING_LONG_BRACKET,
|
|
||||||
end: CLOSING_LONG_BRACKET,
|
|
||||||
contains: [ LONG_BRACKETS ],
|
|
||||||
relevance: 5
|
|
||||||
}
|
|
||||||
])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return lua;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*! `lua` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{
|
|
||||||
const t="\\[=*\\[",a="\\]=*\\]",n={begin:t,end:a,contains:["self"]
|
|
||||||
},r=[e.COMMENT("--(?!"+t+")","$"),e.COMMENT("--"+t,a,{contains:[n],relevance:10
|
|
||||||
})];return{name:"Lua",aliases:["pluto"],keywords:{
|
|
||||||
$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",
|
|
||||||
keyword:"and break do else elseif end for goto if in local not or repeat return then until while",
|
|
||||||
built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"
|
|
||||||
},contains:r.concat([{className:"function",beginKeywords:"function",end:"\\)",
|
|
||||||
contains:[e.inherit(e.TITLE_MODE,{
|
|
||||||
begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",
|
|
||||||
begin:"\\(",endsWithParent:!0,contains:r}].concat(r)
|
|
||||||
},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",
|
|
||||||
begin:t,end:a,contains:[n],relevance:5}])}}})();export default hljsGrammar;
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
/*! `makefile` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Makefile
|
|
||||||
Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
|
|
||||||
Contributors: Joël Porquet <joel@porquet.org>
|
|
||||||
Website: https://www.gnu.org/software/make/manual/html_node/Introduction.html
|
|
||||||
Category: common, build-system
|
|
||||||
*/
|
|
||||||
|
|
||||||
function makefile(hljs) {
|
|
||||||
/* Variables: simple (eg $(var)) and special (eg $@) */
|
|
||||||
const VARIABLE = {
|
|
||||||
className: 'variable',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '\\$\\(' + hljs.UNDERSCORE_IDENT_RE + '\\)',
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
},
|
|
||||||
{ begin: /\$[@%<?\^\+\*]/ }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
/* Quoted string with variables inside */
|
|
||||||
const QUOTE_STRING = {
|
|
||||||
className: 'string',
|
|
||||||
begin: /"/,
|
|
||||||
end: /"/,
|
|
||||||
contains: [
|
|
||||||
hljs.BACKSLASH_ESCAPE,
|
|
||||||
VARIABLE
|
|
||||||
]
|
|
||||||
};
|
|
||||||
/* Function: $(func arg,...) */
|
|
||||||
const FUNC = {
|
|
||||||
className: 'variable',
|
|
||||||
begin: /\$\([\w-]+\s/,
|
|
||||||
end: /\)/,
|
|
||||||
keywords: { built_in:
|
|
||||||
'subst patsubst strip findstring filter filter-out sort '
|
|
||||||
+ 'word wordlist firstword lastword dir notdir suffix basename '
|
|
||||||
+ 'addsuffix addprefix join wildcard realpath abspath error warning '
|
|
||||||
+ 'shell origin flavor foreach if or and call eval file value' },
|
|
||||||
contains: [
|
|
||||||
VARIABLE,
|
|
||||||
QUOTE_STRING // Added QUOTE_STRING as they can be a part of functions
|
|
||||||
]
|
|
||||||
};
|
|
||||||
/* Variable assignment */
|
|
||||||
const ASSIGNMENT = { begin: '^' + hljs.UNDERSCORE_IDENT_RE + '\\s*(?=[:+?]?=)' };
|
|
||||||
/* Meta targets (.PHONY) */
|
|
||||||
const META = {
|
|
||||||
className: 'meta',
|
|
||||||
begin: /^\.PHONY:/,
|
|
||||||
end: /$/,
|
|
||||||
keywords: {
|
|
||||||
$pattern: /[\.\w]+/,
|
|
||||||
keyword: '.PHONY'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/* Targets */
|
|
||||||
const TARGET = {
|
|
||||||
className: 'section',
|
|
||||||
begin: /^[^\s]+:/,
|
|
||||||
end: /$/,
|
|
||||||
contains: [ VARIABLE ]
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
name: 'Makefile',
|
|
||||||
aliases: [
|
|
||||||
'mk',
|
|
||||||
'mak',
|
|
||||||
'make',
|
|
||||||
],
|
|
||||||
keywords: {
|
|
||||||
$pattern: /[\w-]+/,
|
|
||||||
keyword: 'define endef undefine ifdef ifndef ifeq ifneq else endif '
|
|
||||||
+ 'include -include sinclude override export unexport private vpath'
|
|
||||||
},
|
|
||||||
contains: [
|
|
||||||
hljs.HASH_COMMENT_MODE,
|
|
||||||
VARIABLE,
|
|
||||||
QUOTE_STRING,
|
|
||||||
FUNC,
|
|
||||||
ASSIGNMENT,
|
|
||||||
META,
|
|
||||||
TARGET
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return makefile;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*! `makefile` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const a={className:"variable",
|
|
||||||
variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",
|
|
||||||
contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%<?\^\+\*]/}]},i={className:"string",
|
|
||||||
begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,a]},n={className:"variable",
|
|
||||||
begin:/\$\([\w-]+\s/,end:/\)/,keywords:{
|
|
||||||
built_in:"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value"
|
|
||||||
},contains:[a,i]},r={begin:"^"+e.UNDERSCORE_IDENT_RE+"\\s*(?=[:+?]?=)"},s={
|
|
||||||
className:"section",begin:/^[^\s]+:/,end:/$/,contains:[a]};return{
|
|
||||||
name:"Makefile",aliases:["mk","mak","make"],keywords:{$pattern:/[\w-]+/,
|
|
||||||
keyword:"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath"
|
|
||||||
},contains:[e.HASH_COMMENT_MODE,a,i,n,r,{className:"meta",begin:/^\.PHONY:/,
|
|
||||||
end:/$/,keywords:{$pattern:/[\.\w]+/,keyword:".PHONY"}},s]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Markdown
|
|
||||||
Requires: xml.js
|
|
||||||
Author: John Crepezzi <john.crepezzi@gmail.com>
|
|
||||||
Website: https://daringfireball.net/projects/markdown/
|
|
||||||
Category: common, markup
|
|
||||||
*/
|
|
||||||
|
|
||||||
function markdown(hljs) {
|
|
||||||
const regex = hljs.regex;
|
|
||||||
const INLINE_HTML = {
|
|
||||||
begin: /<\/?[A-Za-z_]/,
|
|
||||||
end: '>',
|
|
||||||
subLanguage: 'xml',
|
|
||||||
relevance: 0
|
|
||||||
};
|
|
||||||
const HORIZONTAL_RULE = {
|
|
||||||
begin: '^[-\\*]{3,}',
|
|
||||||
end: '$'
|
|
||||||
};
|
|
||||||
const CODE = {
|
|
||||||
className: 'code',
|
|
||||||
variants: [
|
|
||||||
// TODO: fix to allow these to work with sublanguage also
|
|
||||||
{ begin: '(`{3,})[^`](.|\\n)*?\\1`*[ ]*' },
|
|
||||||
{ begin: '(~{3,})[^~](.|\\n)*?\\1~*[ ]*' },
|
|
||||||
// needed to allow markdown as a sublanguage to work
|
|
||||||
{
|
|
||||||
begin: '```',
|
|
||||||
end: '```+[ ]*$'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '~~~',
|
|
||||||
end: '~~~+[ ]*$'
|
|
||||||
},
|
|
||||||
{ begin: '`.+?`' },
|
|
||||||
{
|
|
||||||
begin: '(?=^( {4}|\\t))',
|
|
||||||
// use contains to gobble up multiple lines to allow the block to be whatever size
|
|
||||||
// but only have a single open/close tag vs one per line
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: '^( {4}|\\t)',
|
|
||||||
end: '(\\n)$'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const LIST = {
|
|
||||||
className: 'bullet',
|
|
||||||
begin: '^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)',
|
|
||||||
end: '\\s+',
|
|
||||||
excludeEnd: true
|
|
||||||
};
|
|
||||||
const LINK_REFERENCE = {
|
|
||||||
begin: /^\[[^\n]+\]:/,
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
className: 'symbol',
|
|
||||||
begin: /\[/,
|
|
||||||
end: /\]/,
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'link',
|
|
||||||
begin: /:\s*/,
|
|
||||||
end: /$/,
|
|
||||||
excludeBegin: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const URL_SCHEME = /[A-Za-z][A-Za-z0-9+.-]*/;
|
|
||||||
const LINK = {
|
|
||||||
variants: [
|
|
||||||
// too much like nested array access in so many languages
|
|
||||||
// to have any real relevance
|
|
||||||
{
|
|
||||||
begin: /\[.+?\]\[.*?\]/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
// popular internet URLs
|
|
||||||
{
|
|
||||||
begin: /\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
|
|
||||||
relevance: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: regex.concat(/\[.+?\]\(/, URL_SCHEME, /:\/\/.*?\)/),
|
|
||||||
relevance: 2
|
|
||||||
},
|
|
||||||
// relative urls
|
|
||||||
{
|
|
||||||
begin: /\[.+?\]\([./?&#].*?\)/,
|
|
||||||
relevance: 1
|
|
||||||
},
|
|
||||||
// whatever else, lower relevance (might not be a link at all)
|
|
||||||
{
|
|
||||||
begin: /\[.*?\]\(.*?\)/,
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returnBegin: true,
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
// empty strings for alt or link text
|
|
||||||
match: /\[(?=\])/ },
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
relevance: 0,
|
|
||||||
begin: '\\[',
|
|
||||||
end: '\\]',
|
|
||||||
excludeBegin: true,
|
|
||||||
returnEnd: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'link',
|
|
||||||
relevance: 0,
|
|
||||||
begin: '\\]\\(',
|
|
||||||
end: '\\)',
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'symbol',
|
|
||||||
relevance: 0,
|
|
||||||
begin: '\\]\\[',
|
|
||||||
end: '\\]',
|
|
||||||
excludeBegin: true,
|
|
||||||
excludeEnd: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const BOLD = {
|
|
||||||
className: 'strong',
|
|
||||||
contains: [], // defined later
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: /_{2}(?!\s)/,
|
|
||||||
end: /_{2}/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /\*{2}(?!\s)/,
|
|
||||||
end: /\*{2}/
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const ITALIC = {
|
|
||||||
className: 'emphasis',
|
|
||||||
contains: [], // defined later
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: /\*(?![*\s])/,
|
|
||||||
end: /\*/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: /_(?![_\s])/,
|
|
||||||
end: /_/,
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// 3 level deep nesting is not allowed because it would create confusion
|
|
||||||
// in cases like `***testing***` because where we don't know if the last
|
|
||||||
// `***` is starting a new bold/italic or finishing the last one
|
|
||||||
const BOLD_WITHOUT_ITALIC = hljs.inherit(BOLD, { contains: [] });
|
|
||||||
const ITALIC_WITHOUT_BOLD = hljs.inherit(ITALIC, { contains: [] });
|
|
||||||
BOLD.contains.push(ITALIC_WITHOUT_BOLD);
|
|
||||||
ITALIC.contains.push(BOLD_WITHOUT_ITALIC);
|
|
||||||
|
|
||||||
let CONTAINABLE = [
|
|
||||||
INLINE_HTML,
|
|
||||||
LINK
|
|
||||||
];
|
|
||||||
|
|
||||||
[
|
|
||||||
BOLD,
|
|
||||||
ITALIC,
|
|
||||||
BOLD_WITHOUT_ITALIC,
|
|
||||||
ITALIC_WITHOUT_BOLD
|
|
||||||
].forEach(m => {
|
|
||||||
m.contains = m.contains.concat(CONTAINABLE);
|
|
||||||
});
|
|
||||||
|
|
||||||
CONTAINABLE = CONTAINABLE.concat(BOLD, ITALIC);
|
|
||||||
|
|
||||||
const HEADER = {
|
|
||||||
className: 'section',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '^#{1,6}',
|
|
||||||
end: '$',
|
|
||||||
contains: CONTAINABLE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '(?=^.+?\\n[=-]{2,}$)',
|
|
||||||
contains: [
|
|
||||||
{ begin: '^[=-]*$' },
|
|
||||||
{
|
|
||||||
begin: '^',
|
|
||||||
end: "\\n",
|
|
||||||
contains: CONTAINABLE
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const BLOCKQUOTE = {
|
|
||||||
className: 'quote',
|
|
||||||
begin: '^>\\s+',
|
|
||||||
contains: CONTAINABLE,
|
|
||||||
end: '$'
|
|
||||||
};
|
|
||||||
|
|
||||||
const ENTITY = {
|
|
||||||
//https://spec.commonmark.org/0.31.2/#entity-references
|
|
||||||
scope: 'literal',
|
|
||||||
match: /&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'Markdown',
|
|
||||||
aliases: [
|
|
||||||
'md',
|
|
||||||
'mkdown',
|
|
||||||
'mkd'
|
|
||||||
],
|
|
||||||
contains: [
|
|
||||||
HEADER,
|
|
||||||
INLINE_HTML,
|
|
||||||
LIST,
|
|
||||||
BOLD,
|
|
||||||
ITALIC,
|
|
||||||
BLOCKQUOTE,
|
|
||||||
CODE,
|
|
||||||
HORIZONTAL_RULE,
|
|
||||||
LINK,
|
|
||||||
LINK_REFERENCE,
|
|
||||||
ENTITY
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return markdown;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/*! `markdown` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar=(()=>{"use strict";return e=>{const n={begin:/<\/?[A-Za-z_]/,
|
|
||||||
end:">",subLanguage:"xml",relevance:0},a={variants:[{begin:/\[.+?\]\[.*?\]/,
|
|
||||||
relevance:0},{
|
|
||||||
begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,
|
|
||||||
relevance:2},{
|
|
||||||
begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/),
|
|
||||||
relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{
|
|
||||||
begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/
|
|
||||||
},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,
|
|
||||||
returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",
|
|
||||||
excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",
|
|
||||||
end:"\\]",excludeBegin:!0,excludeEnd:!0}]},i={className:"strong",contains:[],
|
|
||||||
variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}]
|
|
||||||
},s={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{
|
|
||||||
begin:/_(?![_\s])/,end:/_/,relevance:0}]},c=e.inherit(i,{contains:[]
|
|
||||||
}),t=e.inherit(s,{contains:[]});i.contains.push(t),s.contains.push(c)
|
|
||||||
;let l=[n,a];return[i,s,c,t].forEach((e=>{e.contains=e.contains.concat(l)
|
|
||||||
})),l=l.concat(i,s),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{
|
|
||||||
className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:l},{
|
|
||||||
begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",
|
|
||||||
contains:l}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",
|
|
||||||
end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:l,
|
|
||||||
end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{
|
|
||||||
begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{
|
|
||||||
begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",
|
|
||||||
contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{
|
|
||||||
begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{
|
|
||||||
className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{
|
|
||||||
className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},{scope:"literal",
|
|
||||||
match:/&([a-zA-Z0-9]+|#[0-9]{1,7}|#[Xx][0-9a-fA-F]{1,6});/}]}}})()
|
|
||||||
;export default hljsGrammar;
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
/*! `objectivec` grammar compiled for Highlight.js 11.11.1 */
|
|
||||||
var hljsGrammar = (function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/*
|
|
||||||
Language: Objective-C
|
|
||||||
Author: Valerii Hiora <valerii.hiora@gmail.com>
|
|
||||||
Contributors: Angel G. Olloqui <angelgarcia.mail@gmail.com>, Matt Diephouse <matt@diephouse.com>, Andrew Farmer <ahfarmer@gmail.com>, Minh Nguyễn <mxn@1ec5.org>
|
|
||||||
Website: https://developer.apple.com/documentation/objectivec
|
|
||||||
Category: common
|
|
||||||
*/
|
|
||||||
|
|
||||||
function objectivec(hljs) {
|
|
||||||
const API_CLASS = {
|
|
||||||
className: 'built_in',
|
|
||||||
begin: '\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+'
|
|
||||||
};
|
|
||||||
const IDENTIFIER_RE = /[a-zA-Z@][a-zA-Z0-9_]*/;
|
|
||||||
const TYPES = [
|
|
||||||
"int",
|
|
||||||
"float",
|
|
||||||
"char",
|
|
||||||
"unsigned",
|
|
||||||
"signed",
|
|
||||||
"short",
|
|
||||||
"long",
|
|
||||||
"double",
|
|
||||||
"wchar_t",
|
|
||||||
"unichar",
|
|
||||||
"void",
|
|
||||||
"bool",
|
|
||||||
"BOOL",
|
|
||||||
"id|0",
|
|
||||||
"_Bool"
|
|
||||||
];
|
|
||||||
const KWS = [
|
|
||||||
"while",
|
|
||||||
"export",
|
|
||||||
"sizeof",
|
|
||||||
"typedef",
|
|
||||||
"const",
|
|
||||||
"struct",
|
|
||||||
"for",
|
|
||||||
"union",
|
|
||||||
"volatile",
|
|
||||||
"static",
|
|
||||||
"mutable",
|
|
||||||
"if",
|
|
||||||
"do",
|
|
||||||
"return",
|
|
||||||
"goto",
|
|
||||||
"enum",
|
|
||||||
"else",
|
|
||||||
"break",
|
|
||||||
"extern",
|
|
||||||
"asm",
|
|
||||||
"case",
|
|
||||||
"default",
|
|
||||||
"register",
|
|
||||||
"explicit",
|
|
||||||
"typename",
|
|
||||||
"switch",
|
|
||||||
"continue",
|
|
||||||
"inline",
|
|
||||||
"readonly",
|
|
||||||
"assign",
|
|
||||||
"readwrite",
|
|
||||||
"self",
|
|
||||||
"@synchronized",
|
|
||||||
"id",
|
|
||||||
"typeof",
|
|
||||||
"nonatomic",
|
|
||||||
"IBOutlet",
|
|
||||||
"IBAction",
|
|
||||||
"strong",
|
|
||||||
"weak",
|
|
||||||
"copy",
|
|
||||||
"in",
|
|
||||||
"out",
|
|
||||||
"inout",
|
|
||||||
"bycopy",
|
|
||||||
"byref",
|
|
||||||
"oneway",
|
|
||||||
"__strong",
|
|
||||||
"__weak",
|
|
||||||
"__block",
|
|
||||||
"__autoreleasing",
|
|
||||||
"@private",
|
|
||||||
"@protected",
|
|
||||||
"@public",
|
|
||||||
"@try",
|
|
||||||
"@property",
|
|
||||||
"@end",
|
|
||||||
"@throw",
|
|
||||||
"@catch",
|
|
||||||
"@finally",
|
|
||||||
"@autoreleasepool",
|
|
||||||
"@synthesize",
|
|
||||||
"@dynamic",
|
|
||||||
"@selector",
|
|
||||||
"@optional",
|
|
||||||
"@required",
|
|
||||||
"@encode",
|
|
||||||
"@package",
|
|
||||||
"@import",
|
|
||||||
"@defs",
|
|
||||||
"@compatibility_alias",
|
|
||||||
"__bridge",
|
|
||||||
"__bridge_transfer",
|
|
||||||
"__bridge_retained",
|
|
||||||
"__bridge_retain",
|
|
||||||
"__covariant",
|
|
||||||
"__contravariant",
|
|
||||||
"__kindof",
|
|
||||||
"_Nonnull",
|
|
||||||
"_Nullable",
|
|
||||||
"_Null_unspecified",
|
|
||||||
"__FUNCTION__",
|
|
||||||
"__PRETTY_FUNCTION__",
|
|
||||||
"__attribute__",
|
|
||||||
"getter",
|
|
||||||
"setter",
|
|
||||||
"retain",
|
|
||||||
"unsafe_unretained",
|
|
||||||
"nonnull",
|
|
||||||
"nullable",
|
|
||||||
"null_unspecified",
|
|
||||||
"null_resettable",
|
|
||||||
"class",
|
|
||||||
"instancetype",
|
|
||||||
"NS_DESIGNATED_INITIALIZER",
|
|
||||||
"NS_UNAVAILABLE",
|
|
||||||
"NS_REQUIRES_SUPER",
|
|
||||||
"NS_RETURNS_INNER_POINTER",
|
|
||||||
"NS_INLINE",
|
|
||||||
"NS_AVAILABLE",
|
|
||||||
"NS_DEPRECATED",
|
|
||||||
"NS_ENUM",
|
|
||||||
"NS_OPTIONS",
|
|
||||||
"NS_SWIFT_UNAVAILABLE",
|
|
||||||
"NS_ASSUME_NONNULL_BEGIN",
|
|
||||||
"NS_ASSUME_NONNULL_END",
|
|
||||||
"NS_REFINED_FOR_SWIFT",
|
|
||||||
"NS_SWIFT_NAME",
|
|
||||||
"NS_SWIFT_NOTHROW",
|
|
||||||
"NS_DURING",
|
|
||||||
"NS_HANDLER",
|
|
||||||
"NS_ENDHANDLER",
|
|
||||||
"NS_VALUERETURN",
|
|
||||||
"NS_VOIDRETURN"
|
|
||||||
];
|
|
||||||
const LITERALS = [
|
|
||||||
"false",
|
|
||||||
"true",
|
|
||||||
"FALSE",
|
|
||||||
"TRUE",
|
|
||||||
"nil",
|
|
||||||
"YES",
|
|
||||||
"NO",
|
|
||||||
"NULL"
|
|
||||||
];
|
|
||||||
const BUILT_INS = [
|
|
||||||
"dispatch_once_t",
|
|
||||||
"dispatch_queue_t",
|
|
||||||
"dispatch_sync",
|
|
||||||
"dispatch_async",
|
|
||||||
"dispatch_once"
|
|
||||||
];
|
|
||||||
const KEYWORDS = {
|
|
||||||
"variable.language": [
|
|
||||||
"this",
|
|
||||||
"super"
|
|
||||||
],
|
|
||||||
$pattern: IDENTIFIER_RE,
|
|
||||||
keyword: KWS,
|
|
||||||
literal: LITERALS,
|
|
||||||
built_in: BUILT_INS,
|
|
||||||
type: TYPES
|
|
||||||
};
|
|
||||||
const CLASS_KEYWORDS = {
|
|
||||||
$pattern: IDENTIFIER_RE,
|
|
||||||
keyword: [
|
|
||||||
"@interface",
|
|
||||||
"@class",
|
|
||||||
"@protocol",
|
|
||||||
"@implementation"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
name: 'Objective-C',
|
|
||||||
aliases: [
|
|
||||||
'mm',
|
|
||||||
'objc',
|
|
||||||
'obj-c',
|
|
||||||
'obj-c++',
|
|
||||||
'objective-c++'
|
|
||||||
],
|
|
||||||
keywords: KEYWORDS,
|
|
||||||
illegal: '</',
|
|
||||||
contains: [
|
|
||||||
API_CLASS,
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE,
|
|
||||||
hljs.C_NUMBER_MODE,
|
|
||||||
hljs.QUOTE_STRING_MODE,
|
|
||||||
hljs.APOS_STRING_MODE,
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
begin: '@"',
|
|
||||||
end: '"',
|
|
||||||
illegal: '\\n',
|
|
||||||
contains: [ hljs.BACKSLASH_ESCAPE ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'meta',
|
|
||||||
begin: /#\s*[a-z]+\b/,
|
|
||||||
end: /$/,
|
|
||||||
keywords: { keyword:
|
|
||||||
'if else elif endif define undef warning error line '
|
|
||||||
+ 'pragma ifdef ifndef include' },
|
|
||||||
contains: [
|
|
||||||
{
|
|
||||||
begin: /\\\n/,
|
|
||||||
relevance: 0
|
|
||||||
},
|
|
||||||
hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' }),
|
|
||||||
{
|
|
||||||
className: 'string',
|
|
||||||
begin: /<.*?>/,
|
|
||||||
end: /$/,
|
|
||||||
illegal: '\\n'
|
|
||||||
},
|
|
||||||
hljs.C_LINE_COMMENT_MODE,
|
|
||||||
hljs.C_BLOCK_COMMENT_MODE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
className: 'class',
|
|
||||||
begin: '(' + CLASS_KEYWORDS.keyword.join('|') + ')\\b',
|
|
||||||
end: /(\{|$)/,
|
|
||||||
excludeEnd: true,
|
|
||||||
keywords: CLASS_KEYWORDS,
|
|
||||||
contains: [ hljs.UNDERSCORE_TITLE_MODE ]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
begin: '\\.' + hljs.UNDERSCORE_IDENT_RE,
|
|
||||||
relevance: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectivec;
|
|
||||||
|
|
||||||
})();
|
|
||||||
;
|
|
||||||
export default hljsGrammar;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user