Compare commits

...

1 Commits

Author SHA1 Message Date
Tom Moor 7967f10341 feat: Add XML as code formatting option
Refactor to achieve this
2025-03-24 10:23:15 -04:00
4 changed files with 124 additions and 84 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ import {
import { Decoration, DecorationSet } from "prosemirror-view";
import * as React from "react";
import { v4 } from "uuid";
import { LANGUAGES } from "@shared/editor/extensions/Prism";
import Extension, { WidgetProps } from "@shared/editor/lib/Extension";
import { codeLanguages } from "@shared/editor/lib/code";
import isMarkdown from "@shared/editor/lib/isMarkdown";
import normalizePastedMarkdown from "@shared/editor/lib/markdown/normalize";
import { isRemoteTransaction } from "@shared/editor/lib/multiplayer";
@@ -228,7 +228,7 @@ export default class PasteHandler extends Extension {
state.tr
.replaceSelectionWith(
state.schema.nodes.code_block.create({
language: Object.keys(LANGUAGES).includes(
language: Object.keys(codeLanguages).includes(
vscodeMeta.mode
)
? vscodeMeta.mode
+13 -12
View File
@@ -2,8 +2,11 @@ import { CopyIcon, ExpandedIcon } from "outline-icons";
import { Node as ProseMirrorNode } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
import * as React from "react";
import { LANGUAGES } from "@shared/editor/extensions/Prism";
import { getFrequentCodeLanguages } from "@shared/editor/lib/code";
import {
getFrequentCodeLanguages,
codeLanguages,
getLabelForLanguage,
} from "@shared/editor/lib/code";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
@@ -14,20 +17,19 @@ export default function codeMenuItems(
): MenuItem[] {
const node = state.selection.$from.node();
const allLanguages = Object.entries(LANGUAGES) as [
keyof typeof LANGUAGES,
string
][];
const frequentLanguages = getFrequentCodeLanguages();
const frequentLangMenuItems = frequentLanguages.map((value) => {
const label = LANGUAGES[value];
const label = codeLanguages[value]?.label;
return langToMenuItem({ node, value, label });
});
const remainingLangMenuItems = allLanguages
.filter(([value]) => !frequentLanguages.includes(value))
.map(([value, label]) => langToMenuItem({ node, value, label }));
const remainingLangMenuItems = Object.entries(codeLanguages)
.filter(
([value]) =>
!frequentLanguages.includes(value as keyof typeof codeLanguages)
)
.map(([value, item]) => langToMenuItem({ node, value, label: item.label }));
const languageMenuItems = frequentLangMenuItems.length
? [
@@ -52,8 +54,7 @@ export default function codeMenuItems(
visible: !readOnly,
name: "code_block",
icon: <ExpandedIcon />,
// @ts-expect-error We have a fallback for incorrect mapping
label: LANGUAGES[node.attrs.language ?? "none"],
label: getLabelForLanguage(node.attrs.language ?? "none"),
children: languageMenuItems,
},
];
+4 -59
View File
@@ -4,62 +4,10 @@ import { Node } from "prosemirror-model";
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import refractor from "refractor/core";
import { getPrismLangForLanguage } from "../lib/code";
import { isRemoteTransaction } from "../lib/multiplayer";
import { findBlockNodes } from "../queries/findChildren";
export const LANGUAGES = {
none: "Plain text", // additional entry to disable highlighting
bash: "Bash",
clike: "C",
cpp: "C++",
csharp: "C#",
css: "CSS",
docker: "Docker",
elixir: "Elixir",
erlang: "Erlang",
go: "Go",
graphql: "GraphQL",
groovy: "Groovy",
haskell: "Haskell",
hcl: "HCL",
markup: "HTML",
ini: "INI",
java: "Java",
javascript: "JavaScript",
json: "JSON",
jsx: "JSX",
kotlin: "Kotlin",
lisp: "Lisp",
lua: "Lua",
mermaidjs: "Mermaid Diagram",
nginx: "Nginx",
nix: "Nix",
objectivec: "Objective-C",
ocaml: "OCaml",
perl: "Perl",
php: "PHP",
powershell: "Powershell",
protobuf: "Protobuf",
python: "Python",
r: "R",
ruby: "Ruby",
rust: "Rust",
scala: "Scala",
sass: "Sass",
scss: "SCSS",
sql: "SQL",
solidity: "Solidity",
swift: "Swift",
toml: "TOML",
tsx: "TSX",
typescript: "TypeScript",
vb: "Visual Basic",
verilog: "Verilog",
vhdl: "VHDL",
yaml: "YAML",
zig: "Zig",
};
type ParsedNode = {
text: string;
classes: string[];
@@ -109,12 +57,9 @@ function getDecorations({
blocks.forEach((block) => {
let startPos = block.pos + 1;
const language = (
block.node.attrs.language === "mermaidjs"
? "mermaid"
: block.node.attrs.language
) as string;
if (!language || language === "none" || !refractor.registered(language)) {
const language = getPrismLangForLanguage(block.node.attrs.language);
if (!language || !refractor.registered(language)) {
return;
}
+105 -11
View File
@@ -1,14 +1,97 @@
import Storage from "../../utils/Storage";
import { LANGUAGES } from "../extensions/Prism";
const RecentStorageKey = "rme-code-language";
const StorageKey = "frequent-code-languages";
const frequentLanguagesToGet = 5;
const frequentLanguagesToTrack = 10;
export const FrequentlyUsedCount = {
Get: 5,
Track: 10,
/**
* List of supported code languages.
*
* Object key is the language identifier used in the editor, lang is the
* language identifier used by Prism. Note mismatches such as `markup` and
* `mermaid`.
*/
export const codeLanguages = {
none: { lang: "", label: "Plain text" },
bash: { lang: "bash", label: "Bash" },
clike: { lang: "clike", label: "C" },
cpp: { lang: "cpp", label: "C++" },
csharp: { lang: "csharp", label: "C#" },
css: { lang: "css", label: "CSS" },
docker: { lang: "docker", label: "Docker" },
elixir: { lang: "elixir", label: "Elixir" },
erlang: { lang: "erlang", label: "Erlang" },
go: { lang: "go", label: "Go" },
graphql: { lang: "graphql", label: "GraphQL" },
groovy: { lang: "groovy", label: "Groovy" },
haskell: { lang: "haskell", label: "Haskell" },
hcl: { lang: "hcl", label: "HCL" },
markup: { lang: "markup", label: "HTML" },
ini: { lang: "ini", label: "INI" },
java: { lang: "java", label: "Java" },
javascript: { lang: "javascript", label: "JavaScript" },
json: { lang: "json", label: "JSON" },
jsx: { lang: "jsx", label: "JSX" },
kotlin: { lang: "kotlin", label: "Kotlin" },
lisp: { lang: "lisp", label: "Lisp" },
lua: { lang: "lua", label: "Lua" },
mermaidjs: { lang: "mermaid", label: "Mermaid Diagram" },
nginx: { lang: "nginx", label: "Nginx" },
nix: { lang: "nix", label: "Nix" },
objectivec: { lang: "objectivec", label: "Objective-C" },
ocaml: { lang: "ocaml", label: "OCaml" },
perl: { lang: "perl", label: "Perl" },
php: { lang: "php", label: "PHP" },
powershell: { lang: "powershell", label: "Powershell" },
protobuf: { lang: "protobuf", label: "Protobuf" },
python: { lang: "python", label: "Python" },
r: { lang: "r", label: "R" },
ruby: { lang: "ruby", label: "Ruby" },
rust: { lang: "rust", label: "Rust" },
scala: { lang: "scala", label: "Scala" },
sass: { lang: "sass", label: "Sass" },
scss: { lang: "scss", label: "SCSS" },
sql: { lang: "sql", label: "SQL" },
solidity: { lang: "solidity", label: "Solidity" },
swift: { lang: "swift", label: "Swift" },
toml: { lang: "toml", label: "TOML" },
tsx: { lang: "tsx", label: "TSX" },
typescript: { lang: "typescript", label: "TypeScript" },
vb: { lang: "vb", label: "Visual Basic" },
verilog: { lang: "verilog", label: "Verilog" },
vhdl: { lang: "vhdl", label: "VHDL" },
yaml: { lang: "yaml", label: "YAML" },
xml: { lang: "markup", label: "XML" },
zig: { lang: "zig", label: "Zig" },
};
/**
* Get the human-readable label for a given language.
*
* @param language The language identifier.
* @returns The human-readable label for the language.
*/
export const getLabelForLanguage = (language: keyof typeof codeLanguages) => {
const lang = codeLanguages[language];
return lang ? lang.label : language;
};
/**
* Get the Prism language identifier for a given language.
*
* @param language The language identifier.
* @returns The Prism language identifier for the language.
*/
export const getPrismLangForLanguage = (
language: keyof typeof codeLanguages
): string | undefined => codeLanguages[language].lang;
/**
* Set the most recent code language used.
*
* @param language The language identifier.
*/
export const setRecentCodeLanguage = (language: string) => {
const frequentLangs = (Storage.get(StorageKey) ?? {}) as Record<
string,
@@ -26,14 +109,14 @@ export const setRecentCodeLanguage = (language: string) => {
const frequentLangEntries = Object.entries(frequentLangs);
if (frequentLangEntries.length > FrequentlyUsedCount.Track) {
if (frequentLangEntries.length > frequentLanguagesToTrack) {
sortFrequencies(frequentLangEntries);
const lastEntry = frequentLangEntries[FrequentlyUsedCount.Track];
const lastEntry = frequentLangEntries[frequentLanguagesToTrack];
if (lastEntry[0] === language) {
frequentLangEntries.splice(FrequentlyUsedCount.Track - 1, 1);
frequentLangEntries.splice(frequentLanguagesToTrack - 1, 1);
} else {
frequentLangEntries.splice(FrequentlyUsedCount.Track);
frequentLangEntries.splice(frequentLanguagesToTrack);
}
}
@@ -41,17 +124,28 @@ export const setRecentCodeLanguage = (language: string) => {
Storage.set(RecentStorageKey, language);
};
export const getRecentCodeLanguage = () => Storage.get(RecentStorageKey);
/**
* Get the most recent code language used.
*
* @returns The most recent code language used, or undefined if none is set.
*/
export const getRecentCodeLanguage = () =>
Storage.get(RecentStorageKey) as keyof typeof codeLanguages | undefined;
/**
* Get the most frequent code languages used.
*
* @returns An array of the most frequent code languages used.
*/
export const getFrequentCodeLanguages = () => {
const recentLang = Storage.get(RecentStorageKey);
const frequentLangEntries = Object.entries(Storage.get(StorageKey) ?? {}) as [
keyof typeof LANGUAGES,
keyof typeof codeLanguages,
number
][];
const frequentLangs = sortFrequencies(frequentLangEntries)
.slice(0, FrequentlyUsedCount.Get)
.slice(0, frequentLanguagesToGet)
.map(([lang]) => lang);
const isRecentLangPresent = frequentLangs.includes(recentLang);