इस पेज का अनुवाद मशीन द्वारा किया गया है और इसमें त्रुटियाँ हो सकती हैं। मूल अंग्रेज़ी पढ़ें

एलिमेंट

Editor

Editor एक कोड एडिटर है: पचास से अधिक भाषाओं के लिए सिंटैक्स हाइलाइटिंग, लाइन नंबर, एकाधिक कैरेट, फोल्डिंग, सॉफ्ट रैप, खोजें और बदलें, डायग्नोस्टिक्स और डेकोरेशन, साथ में कोड एडिटरों की कुंजियाँ (पंक्तियों को हिलाना और डुप्लिकेट करना, कमेंट, इंडेंटेशन, और टाइप करते ही बंद होने वाले ब्रैकेट और कोट्स)। यह किसी भी लंबाई की फ़ाइल संभालता है: केवल दिखाई देने वाली पंक्तियाँ बनाई जाती हैं, और हाइलाइटिंग हर संपादन के साथ पंक्ति-दर-पंक्ति आगे बढ़ती है, इसलिए लंबी फ़ाइल भी छोटी फ़ाइल की तरह स्क्रोल होती है और उसमें टाइपिंग उतनी ही सहज रहती है। टाइपिंग प्लेटफ़ॉर्म के इनपुट से होकर जाती है, इसलिए इनपुट मेथड (pinyin, kana, डेड कीज़) और सॉफ्ट कीबोर्ड वैसे ही काम करते हैं जैसे किसी भी टेक्स्ट फ़ील्ड में।

el-editor.tsx
el-editor.tsxLive
import { mount, signal, Text, View } from "nsh";
import { Input } from "nsh/base";
import { Editor, type EditorDiagnostic, type EditorRef, type EditorSearchResult } from "nsh/base";
import { button, buttonText, desktop, field, Log, Panel, Segmented, size, Split, ui, wide } from "./palette";

const SAMPLES = {
  rust: `/// The n-th Fibonacci number.
fn fib(n: u64) -> u64 {
    match n {
        0 | 1 => n,
        _ => fib(n - 1) + fib(n - 2),
    }
}

fn main() {
    let unused = 0;
    for n in 0..10 {
        println!("fib({n}) = {}", fib(n));
    }
}
`,
  typescript: `type Shape = { kind: "circle"; r: number } | { kind: "square"; side: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.r ** 2;
    case "square":
      return shape.side ** 2;
  }
}

console.log(area({ kind: "circle", r: 2 }));
`,
  json: `{
  "name": "my-app",
  "entry": "App.tsx",
  "capabilities": ["network"]
}
`,
} as const;

type Language = keyof typeof SAMPLES;

// A problem to mark in each file, as a linter would report it.
const PROBLEMS: Record<Language, EditorDiagnostic[]> = {
  rust: [{ range: { start: { line: 9, column: 8 }, end: { line: 9, column: 14 } }, severity: "warning", message: "unused variable" }],
  typescript: [],
  json: [],
};

// The text is the editor's own (`defaultValue`), an editor per language so
// each keeps its own undo; the app passes what to find (`search`) and reads
// the caret and the matches from the events.
function App() {
  const language = signal<Language>("rust");
  const wrap = signal<"Scroll" | "Wrap">("Scroll");
  const query = signal("");
  const caret = signal("1:1");
  const found = signal<EditorSearchResult>({ count: 0 });
  const events = signal<string[]>([]);
  const log = (line: string) => events.set([line, ...events()].slice(0, 4));
  let editor: EditorRef | undefined;

  const action = (label: string, onPress: () => void) => (
    <View onPress={onPress} role="button" {...button("secondary", { size: "sm" })}>
      <Text style={buttonText()}>{label}</Text>
    </View>
  );

  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>editor</Text>
      <Text style={{ color: ui().muted }}>
        {desktop ? "Click into the code and type; Alt-click adds a caret, Cmd/Ctrl+D the next match" : "Tap into the code and type"}
      </Text>

      <Split
        grow
        width={640}
        inspector={
          <>
            <Segmented label="language" options={["rust", "typescript", "json"] as const} value={language()} onChange={(v) => language.set(v)} />
            <Segmented label="long lines" options={["Scroll", "Wrap"] as const} value={wrap()} onChange={(v) => wrap.set(v)} />
            <Panel title="find">
              <Input
                value={query()}
                onChange={({ value }) => query.set(value)}
                onSubmit={() => editor?.run("findNext")}
                submitBehavior="submit"
                placeholder="fib"
                accessibilityLabel="Find"
                autoCapitalize="none"
                autoCorrect={false}
                {...field({ style: { height: size.controlSm, paddingVertical: 0 } })}
              />
              <Text style={{ fontSize: 13, fontVariant: ["tabular-nums"], color: ui().text }}>
                {query() ? (found().error ?? `${found().current == null ? "–" : found().current! + 1} of ${found().count}`) : "–"}
              </Text>
              <View style={{ flexDirection: "row", gap: 8 }}>
                {action("Next", () => editor?.run("findNext"))}
                {action("Undo", () => editor?.run("undo"))}
              </View>
            </Panel>
            <Panel title="caret">
              <Text style={{ fontSize: 13, fontVariant: ["tabular-nums"], color: ui().text }}>{caret()}</Text>
            </Panel>
            <Log title="events" lines={events()} minHeight={96} />
          </>
        }
      >
        <View style={{ height: wide() ? 380 : 300, ...ui().card, borderRadius: 14, overflow: "hidden" }}>
          {() => {
            const lang = language();
            return (
              <Editor
                ref={(r) => (editor = r)}
                defaultValue={SAMPLES[lang]}
                language={lang}
                softWrap={wrap() === "Wrap"}
                search={{ query: query() }}
                diagnostics={PROBLEMS[lang]}
                accessibilityLabel="Code"
                onChange={({ value }) => log(`onChange: ${value.length} characters`)}
                onSelectionChange={({ selections }) => {
                  const head = selections[selections.length - 1].head;
                  caret.set(`${head.line + 1}:${head.column + 1}${selections.length > 1 ? ` (${selections.length} carets)` : ""}`);
                }}
                onSearchChange={({ result }) => found.set(result)}
                onFocus={() => log("onFocus")}
                onBlur={() => log("onBlur")}
                onLineNumberPress={({ line }) => log(`onLineNumberPress: ${line + 1}`)}
                onContextMenu={(e) => log(`onContextMenu: ${e.position.line + 1}:${e.position.column + 1}`)}
                style={{ flex: 1 }}
              />
            );
          }}
        </View>
      </Split>
    </View>
  );
}

mount(App);

Editor व्यवहार स्वयं संभालता है और थीम के टोकन से बना अपना एक लुक साथ लाता है: थीम का font-mono 20pt की पंक्तियों में 13pt पर, muted-foreground में लाइन नंबर, कैरेट वाली पंक्तियों के पीछे एक हल्का muted बैंड, primary की एक छटा में सेलेक्शन, border में इंडेंट गाइड, और वे बिल्ट-इन सिंटैक्स रंग जो TextView के कोड ब्लॉक इस्तेमाल करते हैं, लाइट और डार्क दोनों में। हर हिस्सा इन डिफ़ॉल्ट मानों के ऊपर ऐप की स्टाइल लेता है (classes और styles)। Rust में इसे nsh_base::editor() से बनाया जाता है, और TSX में nsh/base से <Editor> के साथ।

use nsh_base::editor;
use nsh_core::prelude::*;

const MAIN: &str = "fn main() {\n    println!(\"Hello, world!\");\n}\n";

fn code() -> impl IntoElement {
    let source = signal(SharedString::from(MAIN));
    editor()
        .value(move || Some(source.get()))
        .language("rust")
        .on_change(move |value: &SharedString| source.set(value.clone()))
        .flex_1()
}

Editor उस बॉक्स को भरता है जो ऐप उसे देता है (flex-1, एक ऊँचाई) और अपना टेक्स्ट उसके भीतर स्क्रोल करता है: ऊर्ध्वाधर हमेशा, क्षैतिज तब जब पंक्तियाँ बॉक्स से लंबी हों, या softWrap के साथ बिल्कुल नहीं, जहाँ लंबी पंक्तियाँ editor की चौड़ाई पर रैप हो जाती हैं।

#टेक्स्ट

  • नियंत्रित (Controlled): value दें और इसे onChange में फिर से सेट करें, जो पूरा नया टेक्स्ट रिपोर्ट करता है। ऐप जब value बदलता है (कोई फ़ाइल लोड हुई, किसी फ़ॉर्मैटर का आउटपुट), तो वह टेक्स्ट को एक ऐसे संपादन के रूप में बदल देता है जिसे undo वापस ले सकता है, और जो कैरेट और फोल्ड बदले हुए हिस्से से दूर हैं वे अपनी जगह रहते हैं।
  • अनियंत्रित (Uncontrolled): defaultValue दें और टेक्स्ट को onChange से या ref के value() से पढ़ें।

पंक्ति-विराम \n हैं: आने वाले टेक्स्ट में \r\n और \r को \n बना दिया जाता है।

#भाषाएँ

language भाषा को उसके नाम, उपनाम (alias), एक्सटेंशन या फ़ाइल नाम से पहचानता है, किसी भी केस में: "rust", "rs", "typescript", "tsx", "main.go", "Dockerfile", "Makefile"। यही तय करता है हाइलाइटिंग, वह कमेंट जिसे mod+/ टॉगल करता है, मेल खाने और इंडेंट करने वाले ब्रैकेट, टाइप करने पर बंद होने वाले जोड़े, शब्द किन अक्षरों से बनते हैं, और डिफ़ॉल्ट इंडेंटेशन: चार स्पेस, वेब भाषाओं (JavaScript, TypeScript, JSON, HTML, CSS, YAML, …) के लिए दो, और Go तथा Makefile के लिए टैब। tabSize और hardTabs हर भाषा के लिए इंडेंटेशन सेट करते हैं। कोई भी अन्य नाम सादा टेक्स्ट माना जाता है, जब तक कि ऐप ने उसे रजिस्टर न किया हो (अपनी भाषाएँ)।

भाषाएँ: Rust, JavaScript, JSX, TypeScript, TSX, JSON, Python, Go, C, C++, C#, Java, Kotlin, Swift, Objective-C, Objective-C++, Ruby, PHP, Lua, Shell, Fish, PowerShell, SQL, HTML, XML, Vue, Svelte, CSS, SCSS, Sass, Less, Markdown, YAML, TOML, INI, Dockerfile, Makefile, CMake, Dart, Scala, Elixir, Erlang, Haskell, OCaml, F#, Clojure, Zig, Perl, R, Julia, Nix, Terraform, Protocol Buffers, GraphQL और Diff।

<Editor value={source()} onChange={({ value }) => source.set(value)} language={file().name} tabSize={2} softWrap class="flex-1" />

#अपनी भाषाएँ

Rust कोड ऐप के editors को और भाषाएँ सिखाता है, और किसी भी भाषा को उसके अपने हाइलाइटर (tree-sitter ग्रामर, ऐप का अपना पार्सर) से हाइलाइट करता है। register_language एक EditorLanguage रजिस्टर करता है: उसका नाम, उपनाम, एक्सटेंशन और फ़ाइल नाम, और उसके संपादन नियम (कमेंट, ब्रैकेट, टाइप करने पर बंद होने वाले जोड़े, शब्द के अक्षर, इंडेंटेशन), जिसमें syntax उस बिल्ट-इन ग्रामर का नाम बताता है जिससे उसे हाइलाइट किया जाए। register_highlighter किसी भी भाषा, बिल्ट-इन या रजिस्टर की हुई, को ऐप का अपना हाइलाइटर देता है: EditorHighlighter को लागू करने वाला एक टाइप, जो हर संपादन सुनता है (पंक्तियाँ, बाइट और पोज़िशन, उसी रूप में जैसे tree-sitter के संपादन लेते हैं) और हर पंक्ति के टोकन (EditorToken, या EditorToken::from_scope के ज़रिए tree-sitter कैप्चर या TextMate स्कोप) एक समय-सीमा के भीतर, UI थ्रेड से बाहर देता है। Editor टोकन को रखता है, संपादन होने पर उन्हें टेक्स्ट के साथ खिसकाता है और दिखाई देने वाली पंक्तियों के लिए फिर से पूछता है, ठीक वैसे ही जैसे वह बिल्ट-इन हाइलाइटर से करता है। language रजिस्टर की हुई भाषाओं को पहले खोजता है, नाम, उपनाम, एक्सटेंशन या पाथ से, editors और diff views दोनों में।

हर engine अपनी भाषाएँ अलग रखता है। Rust ऐप उन्हें अपने root में रजिस्टर करता है, editors माउंट करने से पहले; TSX ऐप के लिए ऐप का crate उन्हें अपने UI सेट के setup में रजिस्टर करता है, जिसे ऐप चलाने वाला हर engine सबसे पहले कॉल करता है।

use nsh_base::editor::{EditorLanguage, register_highlighter, register_language};
use nsh_core::prelude::*;

static NIMBLE: EditorLanguage = EditorLanguage::new("Nimble")
    .extensions(&["nb"])
    .line_comment("#")
    .tab_size(2);

#[native_module("my-app/native")]
fn native(registry: &mut Registry) {
    registry.setup(|| {
        register_language(&NIMBLE);
        register_highlighter("nimble", || Box::new(NimbleHighlighter::default()));
    });
}
<Editor value={source()} onChange={({ value }) => source.set(value)} language="build.nb" class="flex-1" />

#संपादन

Editor वैसे ही काम करता है जैसे कोड एडिटर करते हैं:

  • एकाधिक कैरेट। Alt-क्लिक (Option-क्लिक) एक कैरेट जोड़ता है, mod+d सेलेक्शन की अगली घटना चुनता है, mod+shift+l सभी घटनाएँ चुनता है, और add-caret कुंजियाँ ऊपर या नीचे की पंक्ति पर एक कैरेट जोड़ती हैं। टाइपिंग, डिलीट, मूव और हर कमांड हर कैरेट पर काम करता है; Escape एक कैरेट पर लौटाता है। undoSelection (cmd+u, ctrl+u) पिछली गति से पहले के सेलेक्शन पर लौटाता है।
  • सेलेक्शन इकाइयों में बढ़ते हैं, expandSelection (ctrl+shift+cmd+right, shift+alt+right) से: शब्द, स्ट्रिंग की सामग्री और फिर स्ट्रिंग, ब्रैकेट जोड़े की सामग्री और फिर जोड़ा, पंक्ति, पूरा टेक्स्ट; shrinkSelection उसी रास्ते से वापस ले जाता है। splitSelectionIntoLines (shift+alt+i) कई पंक्तियों में फैले सेलेक्शन को प्रति पंक्ति एक सेलेक्शन में बदल देता है।
  • इंडेंटेशन। नई पंक्ति, पंक्ति का इंडेंटेशन रखती है और खुलने वाले ब्रैकेट के बाद एक स्तर जोड़ती है (Python का :, Ruby का do, …); ब्रैकेट के जोड़े के बीच Enter दबाने पर बंद करने वाला ब्रैकेट अपनी अलग पंक्ति में चला जाता है। Tab और Shift+Tab चुनी हुई पंक्तियों को इंडेंट और आउटडेंट करते हैं; शुरुआती स्पेस में Backspace पिछले इंडेंट स्टॉप पर लौटता है। autoIndent इसे बंद कर देता है।
  • ब्रैकेट और कोट्स टाइप करते ही बंद होते हैं, सेलेक्शन को घेर लेते हैं, और बंद करने वाला दोबारा टाइप करने पर उसके ऊपर से आगे बढ़ जाते हैं (autoClose)। कैरेट वाले ब्रैकेट और उसके जोड़ीदार की आउटलाइन दिखती है।
  • Undo टाइपिंग की पूरी कड़ी को वापस लेता है: एक शब्द, एक डिलीट, एक कमांड। कोई मूव या प्रेस नया चरण शुरू करता है।
  • फोल्डिंग। जिस पंक्ति के बाद अधिक गहराई से इंडेंट की हुई पंक्तियाँ आती हैं, वह उन्हें गटर के मार्कर से फोल्ड कर देती है: फोल्ड करने के लिए ⌄, खोलने के लिए › और पंक्ति के बाद का ⋯। फोल्ड में जाने वाला कैरेट उसे खोल देता है (फोल्डिंग)।
  • प्रेस। पॉइंटर से, क्लिक कैरेट रखता है, डबल क्लिक शब्द चुनता है, ट्रिपल क्लिक पंक्ति, Shift-क्लिक सेलेक्शन बढ़ाता है और ड्रैग उसी इकाई के हिसाब से चुनता है जो क्लिक ने चुनी थी। Alt+Shift ड्रैग (Option+Shift; Linux पर Control+Alt भी) एक ब्लॉक चुनता है: हर पंक्ति में एक सेलेक्शन, उन्हीं कॉलमों के बीच, जो छोटी पंक्तियों के अंत से आगे भी जारी रहते हैं। टच स्क्रीन पर एक टैप कैरेट रखता है, डबल टैप या लॉन्ग प्रेस उँगली के नीचे का शब्द चुनता है, और उस सेलेक्शन के सिरों पर बने हैंडल हर सिरे को तब खिसकाते हैं जब उँगली उन्हें खींचती है (उनके आसपास का टेक्स्ट फिर भी स्क्रोल होता है)।
  • क्लिपबोर्ड। बिना सेलेक्शन के कॉपी और कट कैरेट वाली पूरी पंक्तियाँ लेते हैं, और वे कैरेट की पंक्ति के ऊपर पूरी पंक्तियों के रूप में पेस्ट होती हैं।

readOnly टेक्स्ट को जैसा है वैसा रखता है, जब तक वह चुना हुआ, खोजा हुआ और कॉपी किया हुआ रहता है।

#कुंजियाँ

Editor की कुंजियाँ तभी काम करती हैं जब उस पर फ़ोकस हो; अन्य इनपुट अपनी कुंजियाँ रखते हैं। mod macOS और iOS पर Command है और अन्य जगह Control (keymap की कीस्ट्रोक)।

कुंजियाँ (macOS)कुंजियाँ (Windows, Linux)कमांड
तीर, चुनने के लिए Shift के साथतीर, चुनने के लिए Shift के साथअक्षर और पंक्ति के हिसाब से चलना
alt+left / alt+rightctrl+left / ctrl+rightशब्द के हिसाब से चलना
cmd+left / cmd+right, home / end, ctrl+a / ctrl+ehome / endपंक्ति का पहला गैर-रिक्त अक्षर (फिर उसकी शुरुआत) / पंक्ति का अंत
cmd+up / cmd+downctrl+home / ctrl+endटेक्स्ट की शुरुआत / अंत
pageup / pagedownpageup / pagedownएक पेज ऊपर / नीचे
alt+backspace / alt+deletectrl+backspace / ctrl+deleteबाएँ / दाएँ एक शब्द मिटाना
cmd+backspace / cmd+delete, ctrl+kctrl+shift+backspace / ctrl+shift+deleteपंक्ति की शुरुआत / अंत तक मिटाना
cmd+z / cmd+shift+zctrl+z / ctrl+shift+z, ctrl+yUndo / redo
cmd+actrl+aसब चुनें
cmd+lctrl+lपंक्ति चुनें (दोबारा: अगली भी)
cmd+dctrl+dअगली घटना चुनें
cmd+shift+lctrl+shift+lहर घटना चुनें
cmd+alt+up / cmd+alt+downctrl+alt+up / ctrl+alt+downऊपर / नीचे एक कैरेट जोड़ें
escapeescapeएक सेलेक्शन पर लौटें
alt+up / alt+downalt+up / alt+downपंक्तियों को ऊपर / नीचे खिसकाएँ
alt+shift+up / alt+shift+downalt+shift+up / alt+shift+downपंक्तियों को ऊपर / नीचे डुप्लिकेट करें
cmd+shift+kctrl+shift+kपंक्तियाँ मिटाएँ
cmd+enter / cmd+shift+enterctrl+enter / ctrl+shift+enterनीचे / ऊपर नई पंक्ति
tab / shift+tab, cmd+] / cmd+[tab / shift+tab, ctrl+] / ctrl+[इंडेंट / आउटडेंट
cmd+/ctrl+/लाइन कमेंट टॉगल करें
cmd+shift+\ctrl+shift+\मेल खाने वाले ब्रैकेट पर जाएँ
ctrl+jctrl+jपंक्तियाँ जोड़ें
ctrl+tकैरेट के आसपास के अक्षरों की अदला-बदली करें
ctrl+alt+left / ctrl+alt+right, चुनने के लिए Shift के साथशब्द के हिस्से के हिसाब से चलना (camelCase, snake_case)
shift+alt+ashift+alt+aब्लॉक कमेंट टॉगल करें
shift+alt+ishift+alt+iसेलेक्शन को पंक्तियों में बाँटें
ctrl+shift+cmd+right / ctrl+shift+cmd+leftshift+alt+right / shift+alt+leftसेलेक्शन फैलाएँ / सिकोड़ें
cmd+uctrl+uपिछले सेलेक्शन पर लौटें
cmd+g / cmd+shift+g, f3 / shift+f3f3 / shift+f3अगला / पिछला मैच खोजें
cmd+eसेलेक्शन खोजें (onFindWithSelection)
cmd+alt+[ / cmd+alt+]ctrl+shift+[ / ctrl+shift+]कैरेट की पंक्ति को फोल्ड / अनफोल्ड करें
f12f12कैरेट पर परिभाषा पर जाएँ (onDefinitionRequest)
cmd+.ctrl+.सेलेक्शन पर कोड एक्शन (onCodeActionRequest)
cmd+c / cmd+x / cmd+vctrl+c / ctrl+x / ctrl+vकॉपी / कट / पेस्ट

macOS और iOS में Cocoa की टेक्स्ट कुंजियाँ भी हैं: ctrl+b / ctrl+f / ctrl+p / ctrl+n चलाती हैं, ctrl+h / ctrl+d मिटाती हैं।

जिन कमांड की कोई डिफ़ॉल्ट कुंजी नहीं है, और ऐप के अपने कमांड, वे कुंजियाँ लेते हैं जो ऐप keymap से बाइंड करता है, और उन्हें ref के run से चलाते हैं:

import { keymap } from "nsh";

let editor!: EditorRef;

keymap.bind({
  "f9": () => editor.run("sortLinesAscending"),
  "mod+shift+u": () => editor.run("transformToUppercase"),
  "mod+alt+0": () => editor.run("foldAll"),
  "mod+alt+j": () => editor.run("unfoldAll"),
});

#खोजें और बदलें

search बताता है कि क्या खोजना है: { query, caseSensitive?, wholeWord?, regex?, inSelection? }। इसके मैच हाइलाइट होते हैं, और जिस मैच पर सेलेक्शन है वह अधिक गहरा दिखता है, और onSearchChange { result } रिपोर्ट करता है, जो एक { count, current, error } है: कितने मैच हैं, सेलेक्शन किस मैच पर है (0 से शुरू), और रेगुलर एक्सप्रेशन मान्य क्यों नहीं है। findNext और findPrevious अगला और पिछला मैच चुनते हैं, घूमकर; replace(text) मौजूदा मैच को बदलता है और अगला चुन लेता है, और replaceAll(text) हर मैच को एक संपादन के रूप में बदलता है और { count } लौटाता है, यानी कितने बदले। रेगुलर एक्सप्रेशन का रिप्लेसमेंट अपने ग्रुप को $1 और ${name} के रूप में संदर्भित करता है।

inSelection केवल उसी के भीतर खोजता है जो इसके चालू होते समय चुना गया था (कुछ न चुना हो तो कैरेट की पंक्तियाँ): जब तक यह चालू रहता है, यह दायरा संपादनों के बावजूद बना रहता है, भले ही सेलेक्शन एक मैच से दूसरे मैच पर जाता रहे। findWithSelection (macOS पर cmd+e) चुना हुआ टेक्स्ट, या कैरेट का शब्द, onFindWithSelection के साथ रिपोर्ट करता है, ताकि find bar उसे अपनी क्वेरी बना सके।

Find bar ऐप का अपना होता है:

import { signal, Text, View } from "nsh";
import { Button, Editor, type EditorRef, type EditorSearchResult, Input } from "nsh/base";

function Find(props: { source: string; onChange: (value: string) => void }) {
  let editor!: EditorRef;
  const query = signal("");
  const replacement = signal("");
  const result = signal<EditorSearchResult>({ count: 0 });
  const status = () =>
    result().error ?? (result().current != null ? `${result().current! + 1} of ${result().count}` : `${result().count} found`);
  return (
    <View class="flex-1">
      <View class="flex-row items-center gap-2 border-b border-border p-2">
        <Input value={query()} onChange={({ value }) => query.set(value)} onSubmit={() => editor.run("findNext")} placeholder="Find" class="flex-1" />
        <Text class="text-sm text-muted-foreground">{status()}</Text>
        <Button onPress={() => editor.run("findPrevious")} class="px-2"><Text>↑</Text></Button>
        <Button onPress={() => editor.run("findNext")} class="px-2"><Text>↓</Text></Button>
        <Input value={replacement()} onChange={({ value }) => replacement.set(value)} placeholder="Replace" class="flex-1" />
        <Button onPress={() => editor.replace(replacement())} class="px-2"><Text>Replace</Text></Button>
        <Button onPress={async () => console.log("replaced", (await editor.replaceAll(replacement())).count)} class="px-2"><Text>All</Text></Button>
      </View>
      <Editor
        ref={editor}
        value={props.source}
        onChange={(e) => props.onChange(e.value)}
        language="ts"
        search={query() ? { query: query() } : undefined}
        onSearchChange={(e) => result.set(e.result)}
        class="flex-1"
      />
    </View>
  );
}

#डायग्नोस्टिक्स

diagnostics उन समस्याओं को चिह्नित करता है जो कोई language server या linter रिपोर्ट करता है: हर रेंज के नीचे उसकी गंभीरता (severity) के रंग में एक पतली लहरदार रेखा (error, warning, info, hint)। पोज़िशन 0 से शुरू होने वाली पंक्तियाँ और UTF-16 कॉलम हैं, जैसे language server की, इसलिए उसके डायग्नोस्टिक्स जैसे आते हैं वैसे ही पास हो जाते हैं। उपयोगकर्ता के टाइप करते समय चिह्न अपने टेक्स्ट पर बने रहते हैं, जब तक ऐप नए नहीं देता; संदेश दिखाना (एक सूची, पंक्ति पर hover कार्ड) ऐप का काम है।

एक डायग्नोस्टिक में language server के Diagnostic का बाकी हिस्सा भी होता है। इसके tags उसका टेक्स्ट वैसे ही बनाते हैं जैसे language client करते हैं: unnecessary कोड (अनयूज़्ड वेरिएबल, अप्राप्य कोड) फीका और deprecated कोड पर लकीर खिंची हुई दिखती है, unnecessaryText और deprecatedText हिस्सों में; टैग वाला hint केवल अपने टेक्स्ट में दिखता है। codeDescription.href वह पेज है जो उसके कोड (किसी lint नियम) का वर्णन करता है, और relatedInformation उन अन्य जगहों को सूचीबद्ध करता है जो उसके बारे में बताती हैं ({ location: { uri?, range }, message }): hover दोनों को ऐप को सौंपता है, जो उन्हें अपने hover कार्ड में दिखाता है और पेज खोलता है। Language server टैग संख्याओं (1, 2) के रूप में भेजता है; editor उनके नाम लेता है।

const problems = signal<EditorDiagnostic[]>([]);

async function lint(text: string) {
  // [{ range, severity, message, source?, code?, codeDescription?, tags?, relatedInformation? }]
  problems.set(await check(text));
}

<Editor
  value={source()}
  onChange={({ value }) => {
    source.set(value);
    lint(value);
  }}
  language="ts"
  diagnostics={problems()}
  classes={{ diagnosticWarning: "bg-orange-500", unnecessaryText: "text-zinc-400" }}
  class="flex-1"
/>

#डेकोरेशन

decorations उन रेंज को स्टाइल करते हैं जिन्हें ऐप चुनता है: language server से सिमैंटिक हाइलाइटिंग, चल रहा स्टेटमेंट, वे पंक्तियाँ जो किसी रिव्यू ने बदलीं। डेकोरेशन के class और style उसके टेक्स्ट को स्टाइल करते हैं; lineClass और lineStyle उसकी पंक्तियों की rows को स्टाइल करते हैं, टेक्स्ट के पीछे। डायग्नोस्टिक्स की तरह, वे संपादनों के बावजूद अपने टेक्स्ट पर बने रहते हैं, जब तक ऐप नए सेट नहीं करता।

const range = (line: number, start: number, end: number) => ({ start: { line, column: start }, end: { line, column: end } });

<Editor
  value={source()}
  language="sql"
  readOnly
  decorations={[
    { range: range(4, 0, 0), lineClass: "bg-green-500/10" },
    { range: range(7, 6, 18), class: "underline text-blue-600" },
  ]}
  class="flex-1"
/>

#गटर और ओवरव्यू रूलर मार्क

किसी डेकोरेशन का gutter लाइन नंबरों से पहले की पट्टी में, उसकी रेंज में आने वाली हर लाइन के बगल में एक मार्क लगाता है: वे लाइनें जिन्हें version-control diff ने जोड़ा या बदला, या कोई breakpoint (खाली रेंज अपनी लाइन को कवर करती है, जैसे lineClass के साथ)। placement: "before" के साथ यह रेंज की शुरुआत से पहले की सीमा पर पट्टी के आर-पार एक रेखा होती है, जहाँ लाइनें हटाई गई थीं। पट्टी gutterDecorationsWidth पॉइंट चौड़ी है (डिफ़ॉल्ट रूप से 3) और जब तक किसी डेकोरेशन का गटर मार्क मौजूद है तब तक बनी रहती है, ताकि मार्क आते-जाते रहने पर भी टेक्स्ट अपनी जगह पर रहे। किसी फ़ोल्ड के भीतर की रेंज फ़ोल्ड की पंक्ति को मार्क करती है।

किसी डेकोरेशन का overviewRuler ओवरव्यू रूलर पर मार्क लगाता है। यह एडिटर के दाएँ किनारे की पट्टी है, 14 पॉइंट चौड़ी (स्क्रॉल बार जितनी; overviewRuler भाग दूसरी चौड़ाई तय करता है), जो पूरे टेक्स्ट को उसकी ऊँचाई पर मैप करती है, जैसा Monaco में होता है। मार्क कम से कम 6 पॉइंट ऊँचा होता है और रूलर का एक lane लेता है: left, center (डिफ़ॉल्ट), right या full। जब तक किसी डेकोरेशन का वहाँ मार्क है, रूलर दिखता रहता है।

हर मार्क ऐप के class और style को लेता है, और इनके बिना foreground color में बनता है। दोनों एडिट के साथ वैसे ही चलते हैं जैसे डेकोरेशन की रेंज; जो एडिट किसी मार्क की गई रेंज को मिटा दे, वह उसकी जगह एक खाली रेंज छोड़ देता है।

<Editor
  value={source()}
  onChange={({ value }) => source.set(value)}
  language="ts"
  decorations={[
    { range: range(2, 0, 0), gutter: { class: "bg-yellow-500" }, overviewRuler: { lane: "left", class: "bg-yellow-500" } },
    { range: { start: { line: 3, column: 0 }, end: { line: 5, column: 0 } }, gutter: { class: "bg-green-500" } },
    { range: range(6, 0, 0), gutter: { class: "bg-red-500", placement: "before" }, overviewRuler: { lane: "left", class: "bg-red-500" } },
  ]}
  class="flex-1"
/>

#अपनी हाइलाइटिंग

किसी डेकोरेशन का token अपने टेक्स्ट को वैसे रंगता है जैसे बिल्ट-इन हाइलाइटिंग उस प्रकार के टोकन को रंगती है (comment, keyword, string, number, title, attr, built, deletion), और वहाँ बिल्ट-इन हाइलाइटर ने जो पाया था उसकी जगह लेता है: ऐप का अपना हाइलाइटर (language server के semantic tokens, या ऐप का अपना parser) अपने टोकन को इन पर मैप करता है और लाइट व डार्क दोनों में एडिटर के रंग पाता है। सबसे भीतरी डेकोरेशन का टोकन चलता है; class और style उसके ऊपर लागू होते हैं। highlighting={false} बिल्ट-इन हाइलाइटर को बंद कर देता है, ताकि टेक्स्ट केवल ऐप के टोकन से रंगे, जबकि language अब भी comments, brackets और indentation तय करता है।

const kinds: Record<string, EditorToken> = { keyword: "keyword", function: "title", string: "string", type: "built" };

<Editor
  value={source()}
  onChange={({ value }) => source.set(value)}
  language="ts"
  highlighting={false}
  decorations={semanticTokens().map((t) => ({ range: range(t.line, t.start, t.end), token: kinds[t.type] }))}
  class="flex-1"
/>

#फ़ोल्डिंग

फ़ोल्ड वहाँ से शुरू होते हैं जहाँ लाइनें अपनी पिछली लाइन से अधिक indent की गई हों। foldRanges एडिटर को इसके बजाय ऐप की अपनी रेंज देता है ({ startLine, endLine }, language server की folding ranges का रूप, जो जैसी आती हैं वैसी ही पास हो जाती हैं): लाइन startLine फ़ोल्ड दिखाती है और उसके बाद की endLine तक की लाइनें फ़ोल्ड होकर छिप जाती हैं। जब तक ऐप नई रेंज न दे, वे एडिट के बाद भी अपनी लाइनों पर बनी रहती हैं।

फ़ोल्ड नेस्ट होते हैं: किसी ब्लॉक को फ़ोल्ड करने पर उसके भीतर के फ़ोल्ड फ़ोल्ड ही रहते हैं, इसलिए ब्लॉक को अनफ़ोल्ड करने पर वे वैसे ही दिखते हैं जैसे थे। foldRecursive एक ब्लॉक और उसके भीतर के हर ब्लॉक को फ़ोल्ड करता है, unfoldRecursive उन सबको अनफ़ोल्ड करता है, foldLevel(n) नेस्टिंग स्तर n के हर ब्लॉक को फ़ोल्ड करता है, और foldSelection (या foldRange(range)) किन्हीं भी लाइनों को फ़ोल्ड करता है, चाहे फ़ोल्ड कहीं भी शुरू होते हों। onFoldChange हर बदलाव के बाद फ़ोल्ड की गई रेंज बताता है, और folds() / setFolds(ranges) उन्हें पढ़ते और बहाल करते हैं, ताकि उन्हें फ़ाइल के साथ रखा जा सके।

const ranges = signal<EditorFoldRange[]>([]);
const folded = new Map<string, EditorFoldRange[]>();

<Editor
  ref={editor}
  value={file().text}
  language={file().name}
  foldRanges={ranges()} // from the language server's textDocument/foldingRange
  onFoldChange={({ folds }) => folded.set(file().name, folds)}
  class="flex-1"
/>
<Button onPress={() => editor.foldLevel(2)}><Text>Fold the methods</Text></Button>

#Breakpoints और context menus

लाइन नंबर पर प्रेस करने से onLineNumberPress({ line }) रिपोर्ट होता है, जो उस breakpoint की जगह है जिसे ऐप डेकोरेशन के रूप में खींचता है। सेकेंडरी बटन (राइट क्लिक, Mac पर Control-click) onContextMenu({ position, x, y }) रिपोर्ट करता है: पॉइंटर के नीचे टेक्स्ट में की स्थिति और root view में वह बिंदु, जहाँ ऐप अपना मेन्यू खोलता है। टच स्क्रीन पर लंबा प्रेस, डबल टैप, हैंडल छोड़ना और सेलेक्शन पर टैप भी इसे रिपोर्ट करते हैं, selection के साथ: root view में सेलेक्शन का बॉक्स ({ x, y, width, height }), और उसके ऊपरी मध्य पर x / y, एडिट मेन्यू के लिए।

<Editor
  value={source()}
  onContextMenu={({ x, y, selection }) =>
    Menu.popup(selection ? editItems : contextItems, { x, y })
  }
/>
const breakpoints = signal<number[]>([]);

<Editor
  value={source()}
  language="py"
  onLineNumberPress={({ line }) =>
    breakpoints.set(breakpoints().includes(line) ? breakpoints().filter((l) => l !== line) : [...breakpoints(), line])
  }
  decorations={breakpoints().map((line) => ({ range: range(line, 0, 0), lineClass: "bg-red-500/15" }))}
  class="flex-1"
/>

#भाषा सुविधाएँ

सुझाव, hover जानकारी, go to definition और code actions ऐप के language server से आते हैं। एडिटर completion सूची, inline completion और definition link खुद अपनी कुंजियों के साथ बनाता है; बाकी के लिए वह ऐप को क्षण और स्थान देता है, और ऐप अपने popup खुद बनाता है (Overlay, जो pageBounds से जुड़ा होता है)।

#Completion

completion के साथ, एडिटर ऐप से सुझाव माँगता है और उन्हें टाइप किए जा रहे शब्द के नीचे सूची के रूप में दिखाता है (विंडो के नीचे के पास होने पर उसके ऊपर):

  • शब्द के अंत में कोई अक्षर, अंक या _ टाइप करने पर onCompletionRequest(event) के साथ माँग होती है, trigger "typing": event में उसका id, कैरेट का offset और position, query (अब तक टाइप किया शब्द) और वह queryStart offset होता है जहाँ से वह शुरू होता है। completionTriggerCharacters (["."], ["::", "<"]) में से हर एक के बाद खाली query के साथ माँग होती है, trigger "character" (और character), और showCompletions() या run("showCompletions") कैरेट पर माँगता है, trigger "invoked"।
  • ऐप presentCompletions(event.id, items) से उत्तर देता है। हर आइटम language server के CompletionItem रूप का है: label, और वैकल्पिक रूप से kind ("function", "variable", "class", …), detail, documentation, insertText, filterText, sortText और range। सबसे ताज़ा माँग का उत्तर दिखता है; पहले वाला, या ऐसे शब्द का उत्तर जिसे कैरेट छोड़ चुका है, अलग रख दिया जाता है।
  • टाइप करते रहने पर सूची वहीं फ़िल्टर होती रहती है: query हर आइटम के filterText (नहीं तो उसके label) से अक्षरों के क्रम में, किसी भी केस में मेल खाती है; पहले वे शब्द जो query से शुरू होते हैं, फिर सबसे नज़दीकी मेल, फिर sortText के अनुसार। मेल खाए अक्षर उभरकर दिखते हैं। टाइप करते रहने पर एडिटर दोबारा माँगे, इसके लिए incomplete: true (presentCompletions(id, items, true)) दें।
  • Up और Down (तथा Control+P / Control+N) सूची में चलते हैं, Page Up और Page Down एक पेज के हिसाब से, Enter और Tab स्वीकार करते हैं, Escape बंद करता है। पॉइंटर किसी आइटम को चिह्नित करता है और क्लिक (टच स्क्रीन पर टैप) उसे स्वीकार करता है। चुने गए आइटम का documentation सूची के बगल में दिखता है।
  • स्वीकार करने पर आइटम का range (नहीं तो query) उसके insertText (नहीं तो label) से बदल जाता है, एक ऐसे चरण के रूप में जिसे undo वापस ले सकता है, कैरेट पर और हर उस दूसरे कैरेट पर जिसके पहले वही टेक्स्ट है, और onCompletionAccept({ item }) रिपोर्ट होता है।
  • कैरेट के शब्द से बाहर जाने पर, space या अन्य punctuation पर, एडिटर का फ़ोकस खोने पर और कैरेट के दृश्य से बाहर स्क्रॉल होने पर सूची बंद हो जाती है।

टाइपिंग में विराम (inlineCompletionDelay, डिफ़ॉल्ट रूप से 300 ms) पर, जब कोई सूची न दिख रही हो, inline completion माँगा जाता है: onInlineCompletionRequest({ id, offset, position })। presentInlineCompletion(id, text) कैरेट पर text को हल्के रंग में दिखाता है, Tab उसे डालता है, और Escape, कोई एडिट या कैरेट का हिलना उसे हटा देता है।

let editor!: EditorRef;

<Editor
  ref={(r) => (editor = r)}
  value={source()}
  onChange={({ value }) => source.set(value)}
  language="ts"
  completion
  completionTriggerCharacters={["."]}
  onCompletionRequest={async (e) => {
    const items = await server.completion(e.position);
    editor.presentCompletions(e.id, items);
  }}
  onInlineCompletionRequest={async (e) => {
    editor.presentInlineCompletion(e.id, await assistant.suggest(e.offset));
  }}
  class="flex-1"
/>

#Hovers और presses

  • onHover(event): माउस किसी अक्षर पर टिका है और कोई बटन दबा नहीं है (डेस्कटॉप और वेब; ड्रैग करने पर टेक्स्ट सेलेक्ट होता है)। event में उसका position, उसका UTF-16 offset, उसके शब्द का range, पॉइंटर का pagePosition, शब्द का pageBounds, दबे हुए modifiers और वहाँ के diagnostics होते हैं, जिनके संदेश ऐप दिखाता है। दूसरे शब्द पर जाने पर अगला hover तुरंत रिपोर्ट होता है।
  • onHoverEnd(): माउस शब्द या एडिटर से बाहर गया, या उसके नीचे टेक्स्ट बदला या स्क्रॉल हुआ।
  • onTextPress(event): एडिटर के कैरेट रखने के बाद कोई अक्षर प्रेस किया गया: { position, offset, range, pagePosition, modifiers, clicks }।
  • caretBounds() और rangeBounds(range): कैरेट और कोई रेंज कहाँ हैं, { bounds, pageBounds } (एडिटर में और root view में): वहाँ ऐप का popup खुलता है।
const hover = signal<EditorHoverEvent | null>(null);

<Editor
  ref={editor}
  value={source()}
  language="ts"
  onHover={(event) => hover.set(event)}
  onHoverEnd={() => hover.set(null)}
  class="flex-1"
/>
<If when={hover()}>
  {(event) => (
    <Overlay anchor={event().pageBounds} placement="top" class="rounded-md border border-border bg-popover p-2">
      <Text>{event().diagnostics.map((d) => d.message).join("\n") || describe(event().range)}</Text>
    </Overlay>
  )}
</If>

#Go to definition और code actions

Mod दबाए रखने (macOS और iOS पर Command, बाकी जगह Control) से माउस के नीचे के शब्द लिंक बन जाते हैं, जैसे code editors में:

  • किसी शब्द पर Mod दबाए रखने पर (माउस के वहाँ टिके रहते दबाया गया, या Mod दबाए हुए माउस वहाँ ले जाया गया), एडिटर onDefinitionRequest(event) से माँगता है, trigger "hover": event में उसका id, अक्षर का position और offset, और शब्द का range होता है। जब language server को definition मिले तो presentDefinitionLink(event.id, range) से उत्तर दें: रेंज रेखांकित हो जाती है (definitionLink भाग) और उस पर पॉइंटर हाथ बन जाता है। Mod छोड़ने पर, माउस के लिंक से हटने पर, टेक्स्ट बदलने या स्क्रॉल होने पर, या hideDefinitionLink() से लिंक हट जाता है। सबसे ताज़ा माँग का उत्तर दिखता है; पहले वाला अलग रख दिया जाता है।
  • किसी शब्द पर Mod-click trigger "press" के साथ माँगता है; लिंक पर कैरेट वहीं रहता है जहाँ था। F12 (goToDefinition, run("goToDefinition")) कैरेट वाले शब्द के लिए माँगता है, trigger "key"। ऐप definition पर जाता है: उसी टेक्स्ट में हो तो revealPosition और setSelections, दूसरी फ़ाइल के लिए अपना नेविगेशन।
  • Mod+. (showCodeActions, run("showCodeActions")) onCodeActionRequest({ range, position, bounds }) रिपोर्ट करता है: प्राथमिक सेलेक्शन (शुरुआत पहले), कैरेट और उसका बॉक्स। ऐप अपने language server से actions माँगता है, अपना मेन्यू bounds.pageBounds पर दिखाता है (Menu या Overlay), और चुनी गई action के एडिट edit(edits) से लागू करता है।
let editor!: EditorRef;

<Editor
  ref={(r) => (editor = r)}
  value={source()}
  onChange={({ value }) => source.set(value)}
  language="ts"
  onDefinitionRequest={async (e) => {
    const target = await server.definition(e.position);
    if (!target) return;
    if (e.trigger === "hover") editor.presentDefinitionLink(e.id, target.originRange ?? e.range);
    else editor.revealPosition(target.range.start, true);
  }}
  onCodeActionRequest={async (e) => {
    const actions = await server.codeActions(e.range);
    const picked = await choose(actions, e.bounds?.pageBounds);
    if (picked) editor.edit(picked.edits);
  }}
  class="flex-1"
/>

#आकार और स्क्रॉलिंग

minRows और / या maxRows के साथ एडिटर अपने टेक्स्ट के साथ बढ़ता है: वह अपनी पंक्तियों जितना ऊँचा होता है, कम से कम minRows पंक्तियाँ, और maxRows से आगे उसका टेक्स्ट स्क्रॉल होता है, जैसे message box या notebook का cell। अन्यथा आकार ऐप तय करता है, और scrollBeyondLastLine टेक्स्ट को अंतिम लाइन के आगे तक स्क्रॉल होने देता है: एक स्क्रीन तक (true, डिफ़ॉल्ट), बिल्कुल नहीं (false) या कुछ पंक्तियों तक। कैरेट के हिलने पर उसके आसपास cursorSurroundingLines पंक्तियाँ दृश्य में रहती हैं। सेलेक्शन को एडिटर के किनारों के पार खींचने पर स्क्रॉल आगे बढ़ता है, पॉइंटर जितना दूर जाता है उतना तेज़।

showWhitespaces हर space के लिए · और हर tab के लिए → दिखाता है (whitespace भाग), और rulers अपने हर column पर एक रेखा खींचता है (ruler भाग), लाइन की लंबाई के guides के रूप में।

<Editor
  defaultValue={message()}
  language="markdown"
  minRows={2}
  maxRows={8}
  rulers={[72]}
  showWhitespaces
/>

#Methods

ref={editor} एडिटर के methods देता है (EditorRef), ऐप के बटनों, मेन्यू और कुंजियों के लिए। जो methods कोई मान लौटाते हैं (value(), selections(), replaceAll(), canUndo(), caretBounds(), folds(), …) वे JS में उसका Promise लौटाते हैं, इसलिए उन्हें await करें: const text = await editor.value()। जिस method को टेक्स्ट के बाद की लाइन दी जाए (fold, scrollToLine, goToLine, …) वह कुछ नहीं बदलता और method व लाइन का नाम बताते हुए चेतावनी लॉग करता है।

Methodविवरण
focus(), blur()फ़ोकस लेता है, या छोड़ देता है।
value()Promise<string>: टेक्स्ट।
setValue(value)टेक्स्ट बदलता है, एक ऐसे एडिट के रूप में जिसे undo वापस ले सकता है।
insert(text)हर सेलेक्शन पर text को जैसा है वैसा टाइप करता है (brackets बंद नहीं होते)।
edit(edits){ range, text } replacements लागू करता है (वर्तमान टेक्स्ट की रेंज; जो किसी पहले वाली से ओवरलैप करे वह छोड़ दी जाती है) एक ऐसे चरण के रूप में जिसे undo वापस ले सकता है; सेलेक्शन अपने आसपास के टेक्स्ट के साथ चलते हैं। Code actions और formatting edits यहीं जाते हैं।
selections()Promise<EditorSelection[]>: हर सेलेक्शन, { anchor, head }, प्राथमिक (सबसे नया) अंत में।
setSelections(selections)सेलेक्शन सेट करता है (अंतिम वाला प्राथमिक) और प्राथमिक को दृश्य में स्क्रॉल करता है।
run(command)कमांड चलाता है, जैसे उसकी कुंजियाँ चलातीं (कमांड)।
replace(text)वर्तमान मेल को बदलता है और अगला चुनता है।
replaceAll(text)Promise<EditorReplaceAll>: हर मेल को एक एडिट के रूप में बदलता है; { count }, कितने।
fold(line), unfold(line)लाइन line (0 से) के नीचे की लाइनों को फ़ोल्ड करता है, या अनफ़ोल्ड करता है।
foldRecursive(line), unfoldRecursive(line)लाइन line का फ़ोल्ड और उसके भीतर के हर फ़ोल्ड को फ़ोल्ड करता है, या उन सबको अनफ़ोल्ड करता है।
foldLevel(level)नेस्टिंग स्तर level (1: सबसे बाहरी) के हर फ़ोल्ड को फ़ोल्ड करता है।
foldRange(range)range ({ startLine, endLine }) की लाइनों को फ़ोल्ड करता है, फ़ोल्ड कहीं भी शुरू होते हों।
folds()Promise<EditorFoldRange[]>: फ़ोल्ड की गई रेंज, नेस्टेड वाली सहित।
setFolds(folds)ठीक folds को फ़ोल्ड करता है।
scrollToLine(line)लाइन line को दृश्य में स्क्रॉल करता है, जो उसे छिपा रहा हो उसे अनफ़ोल्ड करते हुए।
canUndo(), canRedo()Promise<boolean>: क्या वापस लेने के लिए, या दोबारा करने के लिए कोई एडिट है।
caretBounds()Promise<EditorBounds | null>: प्राथमिक कैरेट का बॉक्स, { bounds, pageBounds }, एडिटर में (उसकी स्क्रॉलिंग लागू करके) और root view में; एडिटर का layout होने से पहले null।
rangeBounds(range)Promise<EditorBounds[]>: range के बॉक्स, उसकी हर कवर की गई पंक्ति के लिए एक।
scrollOffset(), setScrollOffset(offset)Promise<Point>: टेक्स्ट कितना स्क्रॉल हुआ है; उसे { x, y } तक स्क्रॉल करता है।
visibleRange()Promise<EditorRange>: viewport जो टेक्स्ट दिखाता है, उसकी पहली पंक्ति की शुरुआत से अंतिम पंक्ति के अंत तक।
selectedText()Promise<string>: प्राथमिक सेलेक्शन का टेक्स्ट।
offsetToPosition(offset), positionToOffset(position)Promise<EditorPosition>, Promise<number>: value() के UTF-16 offsets और positions के बीच।
revealPosition(position, center)position को दृश्य में स्क्रॉल करता है, जो उसे छिपा रहा हो उसे अनफ़ोल्ड करते हुए; center के साथ बीच में।
goToLine(line)कैरेट को लाइन line (0 से) की शुरुआत पर रखता है और उसे बीच में स्क्रॉल करता है।
showCompletions()कैरेट पर completions माँगता है, उससे पहले का शब्द query होता है (Completion)।
presentCompletions(id, items, incomplete?)completion माँग id का उत्तर देता है: सूची items दिखाती है, उसके बाद टाइप की गई चीज़ से फ़िल्टर होकर; incomplete के साथ, टाइप करते रहने पर दोबारा माँगा जाता है। कोई आइटम न हो तो सूची बंद हो जाती है।
hideCompletions()completion सूची बंद करता है।
presentInlineCompletion(id, text), hideInlineCompletion()inline completion माँग id का उत्तर text से देता है, जो कैरेट पर दिखता है; उसे हटाता है।
presentDefinitionLink(id, range), hideDefinitionLink()definition माँग id (trigger "hover") का उत्तर देता है: range लिंक के रूप में रेखांकित होता है (Go to definition); उसे छिपाता है।
let editor!: EditorRef;

<Button onPress={() => editor.run("toggleComment")}><Text>Comment</Text></Button>
<Button onPress={async () => save(await editor.value())}><Text>Save</Text></Button>
<Button onPress={async () => (await editor.canUndo()) && editor.run("undo")}><Text>Undo</Text></Button>
<Button onPress={() => editor.setSelections([{ anchor: { line: 0, column: 0 }, head: { line: 0, column: 0 } }])}>
  <Text>Go to the top</Text>
</Button>
<Editor ref={editor} value={source()} onChange={({ value }) => source.set(value)} language="rs" class="flex-1" />

#कमांड

run एडिटर के कमांड उनके नाम से लेता है (EditorCommand):

कमांडये क्या करते हैं
moveLeft, moveRight, moveUp, moveDown, moveWordLeft, moveWordRight, moveLineStart, moveLineEnd, movePageUp, movePageDown, moveDocumentStart, moveDocumentEndकैरेट को घुमाते हैं।
selectLeft, selectRight, selectUp, selectDown, selectWordLeft, selectWordRight, selectLineStart, selectLineEnd, selectPageUp, selectPageDown, selectDocumentStart, selectDocumentEndसेलेक्शन को बढ़ाते हैं।
moveToMatchingBracketकैरेट वाले ब्रैकेट के मिलान वाले ब्रैकेट पर जाता है।
selectAll, selectLine, selectWord, selectNextOccurrence, selectPreviousOccurrence, selectAllOccurrencesचुनते हैं।
moveSubwordLeft, moveSubwordRight, selectSubwordLeft, selectSubwordRightशब्द के एक हिस्से के हिसाब से घुमाते या बढ़ाते हैं (camelCase के हम्प, snake_case के हिस्से)।
moveParagraphUp, moveParagraphDown, selectParagraphUp, selectParagraphDownपैराग्राफ़ से पहले या बाद की खाली लाइन तक घुमाते या बढ़ाते हैं।
expandSelection, shrinkSelectionसेलेक्शन को एक इकाई से बढ़ाते हैं, या वापस घटाते हैं।
undoSelection, redoSelectionपिछली मूवमेंट से पहले के सेलेक्शन पर लौटते हैं, या दोबारा आगे जाते हैं।
splitSelectionIntoLinesहर सेलेक्शन की हर लाइन के लिए एक सेलेक्शन।
addCaretAbove, addCaretBelow, singleSelectionएक कैरेट जोड़ते हैं; वापस एक सेलेक्शन पर लौटते हैं।
deleteBackward, deleteForward, deleteWordLeft, deleteWordRight, deleteToLineStart, deleteToLineEnd, deleteLineहटाते हैं।
newline, newlineBelow, newlineAbove, indent, outdent, toggleComment, toggleBlockCommentलाइनें, इंडेंटेशन और कमेंट।
sortLinesAscending, sortLinesDescendingचुनी हुई लाइनों को क्रमबद्ध करते हैं (जिस लाइन में सिर्फ़ कैरेट हो, वह भी हर लाइन)।
transformToUppercase, transformToLowercase, transformToTitleCaseसेलेक्शन का केस बदलते हैं (कैरेट हो तो उसका शब्द)।
trimTrailingWhitespaceहर लाइन के अंत की व्हाइटस्पेस हटाता है।
moveLinesUp, moveLinesDown, duplicateLinesUp, duplicateLinesDown, joinLines, transposeCharactersफिर से व्यवस्थित करते हैं।
undo, redoपूर्ववत करें और फिर से करें।
fold, unfold, foldAll, unfoldAllकैरेट की लाइन पर, या सब कुछ फ़ोल्ड करते हैं।
foldRecursive, unfoldRecursiveकैरेट का फ़ोल्ड और उसके अंदर के हर फ़ोल्ड को फ़ोल्ड करते हैं, या खोलते हैं।
foldSelectionहर उस सेलेक्शन की लाइनें फ़ोल्ड करता है जो कई लाइनों में फैला हो।
findNext, findPrevioussearch का अगला या पिछला मिलान चुनते हैं।
findWithSelectionफ़ाइंड बार (onFindWithSelection) के लिए चुना हुआ टेक्स्ट रिपोर्ट करता है।
copy, cut, pasteक्लिपबोर्ड।
showCompletionsकैरेट पर कम्प्लीशन माँगता है (कंप्लीशन)।
goToDefinitionकैरेट वाले शब्द की डेफ़िनिशन माँगता है (onDefinitionRequest, ट्रिगर "key")।
showCodeActionsसेलेक्शन पर कोड एक्शन माँगता है (onCodeActionRequest)।

#हिस्सों की स्टाइलिंग

class और style एडिटर के बॉक्स को स्टाइल करते हैं। classes हिस्से के अनुसार Tailwind क्लास लेता है और styles हिस्से के अनुसार इनलाइन स्टाइल; किसी हिस्से की क्लास उसकी अपनी डिफ़ॉल्ट स्टाइल के बाद आती हैं, इसलिए वही लागू होती हैं, और उसके styles क्लास पर भारी पड़ते हैं। फ़ॉन्ट fontFamily, fontSize और lineHeight से सेट होता है, जिनमें एडिटर अपना टेक्स्ट नापता है।

<Editor
  value={source()}
  onChange={({ value }) => source.set(value)}
  language="tsx"
  fontFamily="JetBrains Mono"
  fontSize={14}
  lineHeight={22}
  class="flex-1 rounded-lg border border-border"
  classes={{
    gutter: "bg-muted",
    lineNumber: "text-xs",
    activeLine: "bg-yellow-500/10",
    selection: "bg-blue-500/30",
  }}
  styles={{ caret: { backgroundColor: "#f59e0b", width: 3 } }}
/>
हिस्सायह क्या हैडिफ़ॉल्ट
gutterलाइन नंबरों और फ़ोल्ड मार्करों का कॉलमनंबरों से पहले 12pt, मार्करों के बाद 4pt
lineNumberहर लाइन नंबरदाएँ संरेखित, muted-foreground
activeLineNumberकैरेट वाली लाइनों के नंबर, lineNumber के बादforeground
foldMarkerउस लाइन का मार्कर जहाँ फ़ोल्ड शुरू होता है⌄, फ़ोल्ड होने पर ›, muted-foreground
overviewRulerदाएँ किनारे पर ओवरव्यू रूलर, जब वहाँ किसी डेकोरेशन का मार्क होकोई नहीं
contentगटर के बगल का टेक्स्ट क्षेत्रटेक्स्ट से पहले 4pt
lineटेक्स्ट की हर पंक्ति
activeLineकैरेट वाली लाइनों की पंक्तियों के पीछे, स्क्रॉल संकेतकों के नीचेmuted, हल्का
selectionचुने हुए टेक्स्ट के पीछेprimary की एक झलक
caretकैरेट2pt चौड़े, foreground, झपकते हुए
compositionवह टेक्स्ट जिसे इनपुट मेथड कंपोज़ कर रहा हैरेखांकित
searchMatchsearch के मिलानपारदर्शी एम्बर
currentSearchMatchवह मिलान जिस पर सेलेक्शन है, searchMatch के बादगाढ़ा एम्बर
matchingBracketकैरेट वाला ब्रैकेट और उसका जोड़ीदार1pt का बॉर्डर
indentGuideहर इंडेंटेशन स्तर के लिए एक गाइडborder की 1pt लाइन
foldPlaceholderफ़ोल्ड की गई लाइन अपने टेक्स्ट के बाद क्या दिखाती है⋯, muted-foreground
placeholderटेक्स्ट खाली होने पर दिखता हैmuted-foreground
diagnosticErrorएरर के नीचे की रेखाdestructive
diagnosticWarningवॉर्निंग के नीचे की रेखाएम्बर
diagnosticInfoइन्फ़ॉर्मेशन के नीचे की रेखानीला
diagnosticHintहिंट के नीचे की रेखाmuted-foreground
unnecessaryTextunnecessary डायग्नोस्टिक का टेक्स्टmuted-foreground
deprecatedTextdeprecated डायग्नोस्टिक का टेक्स्टकटा हुआ
whitespaceस्पेस और टैब के निशान (showWhitespaces)· और →, 60 % पर muted-foreground
rulerrulers की रेखाएँborder की 1 पॉइंट की रेखा
selectionHandleटच सेलेक्शन के सिरों पर हैंडल की घुंडियाँprimary का 10pt का बिंदु
selectionHandleStart, selectionHandleEndशुरुआत की घुंडी (उसकी लाइन के ऊपर) और अंत की (नीचे), selectionHandle के बाद
completionMenuकम्प्लीशन सूची का बॉक्सpopover, 1pt का border, गोल कोने, एक शैडो
completionItemसूची का हर आइटमएक पंक्ति जितनी ऊँचाई, 8pt पैडिंग
selectedCompletionItemचुना हुआ आइटम, completionItem के बादaccent
completionMatchलेबल के वे अक्षर जिनसे क्वेरी मिलीसेमीबोल्ड, primary
completionDetailआइटम का विवरण, उसके लेबल के बाददाएँ संरेखित, muted-foreground
completionKindआइटम की किस्म, उसके लेबल से पहलेएक छोटा नाम (fn, var, cls), muted-foreground
completionDocumentationचुने हुए आइटम का डॉक्यूमेंटेशन, सूची के बगल मेंcompletionMenu जैसा, अधिकतम 320pt चौड़ा
inlineCompletionकैरेट पर इनलाइन कम्प्लीशनmuted-foreground
definitionLinkडेफ़िनिशन लिंक की अंडरलाइनटेक्स्ट के नीचे primary की 1pt रेखा

#Props

PropRustटाइपडिफ़ॉल्टविवरण
value.value(..)stringटेक्स्ट, जिसे ऐप नियंत्रित करता है (इसे onChange से दोबारा सेट करें)।
defaultValue.default_value(..)stringपहला टेक्स्ट, जब एडिटर अपना टेक्स्ट खुद रखता है।
language.language(..)stringसादा टेक्स्टकिसी भाषा का नाम, उपनाम, एक्सटेंशन या फ़ाइल नाम (भाषाएँ)।
placeholder.placeholder(..)stringटेक्स्ट खाली होने पर दिखता है।
readOnly.read_only(..)booleanfalseटेक्स्ट चुना और कॉपी किया जा सकता है, बदला नहीं जा सकता।
autoFocus.auto_focus(..)booleanfalseदिखाई देते ही फ़ोकस ले लेता है।
lineNumbers.line_numbers(..)booleantrueगटर के लाइन नंबर।
softWrap.soft_wrap(..)booleanfalseलंबी लाइनें एडिटर की चौड़ाई पर रैप होती हैं।
tabSize.tab_size(..)numberभाषा का डिफ़ॉल्टएक टैब और एक इंडेंटेशन स्तर के कॉलम।
hardTabs.hard_tabs(..)booleanभाषा का डिफ़ॉल्टटैब से इंडेंट करता है।
folding.folding(..)booleantrueइंडेंट की गई लाइनें गटर के मार्करों से फ़ोल्ड होती हैं।
foldRanges.fold_ranges(..)EditorFoldRange[]इंडेंटेशन के अनुसारफ़ोल्ड कहाँ शुरू होते हैं, ऐप के अनुसार (फ़ोल्डिंग)।
highlighting.highlighting(..)booleantrueअंतर्निहित सिंटैक्स हाइलाइटिंग (अपनी हाइलाइटिंग)।
autoClose.auto_close(..)booleantrueब्रैकेट और कोट टाइप करते ही बंद हो जाते हैं।
autoIndent.auto_indent(..)booleantrueनई लाइन इंडेंटेशन बनाए रखती है और खुलने वाले ब्रैकेट के बाद इंडेंट करती है।
activeLine.active_line(..)booleantrueकैरेट वाली लाइनों की पंक्तियाँ हाइलाइट होती हैं।
indentGuides.indent_guides(..)booleantrueइंडेंटेशन के गाइड।
scrollBeyondLastLine.scroll_beyond_last_line(..)boolean | numbertrueटेक्स्ट अपनी आख़िरी लाइन के आगे भी स्क्रॉल होता है: एक स्क्रीन तक (true), बिल्कुल नहीं (false) या उतनी पंक्तियों तक।
fontFamily.font_family(..)stringथीम का font-monoवह फ़ॉन्ट जिसमें टेक्स्ट बनाया और नापा जाता है।
fontSize.font_size(..)number13फ़ॉन्ट का आकार, पॉइंट में।
lineHeight.line_height(..)number20एक पंक्ति की ऊँचाई, पॉइंट में।
search.search(..)EditorSearchक्या खोजना है (खोजें और बदलें)।
diagnostics.diagnostics(..)EditorDiagnostic[][]चिह्नित की जाने वाली समस्याएँ (डायग्नोस्टिक्स)।
decorations.decorations(..)EditorDecoration[][]वे रेंज जिन्हें ऐप स्टाइल करता है (डेकोरेशन)।
gutterDecorationsWidth.gutter_decorations_width(..)number3गटर में डेकोरेशन के मार्क की पट्टी की चौड़ाई, पॉइंट में।
classes.classes(..)EditorClasses{}हिस्से के अनुसार Tailwind क्लास।
styles.styles(..)EditorStyles{}हिस्से के अनुसार इनलाइन स्टाइल, क्लास के ऊपर।
accessibilityLabel.accessibility_label(..)stringस्क्रीन रीडर एडिटर को किस नाम से पुकारते हैं।
showWhitespaces.show_whitespaces(..)booleanfalseस्पेस और टैब · और → के रूप में दिखते हैं।
rulers.rulers(..)number[][]वे कॉलम जहाँ खड़ी रेखा खींचनी है।
minRows.min_rows(..)numberएडिटर अपने टेक्स्ट के साथ इतनी पंक्तियों से बढ़ना शुरू करता है (आकार और स्क्रॉलिंग)।
maxRows.max_rows(..)numberएडिटर अपने टेक्स्ट के साथ अधिकतम इतनी पंक्तियों तक बढ़ता है, फिर स्क्रॉल करता है।
cursorSurroundingLines.cursor_surrounding_lines(..)number1कैरेट के ऊपर और नीचे दृश्य में रखी जाने वाली पंक्तियाँ।
completion.completion(..)booleanfalseएडिटर सुझाव माँगता है और उन्हें सूची के रूप में दिखाता है (कंप्लीशन)।
completionTriggerCharacters.completion_trigger_characters(..)string[][]टाइप करते समय और क्या कम्प्लीशन माँगता है।
inlineCompletionDelay.inline_completion_delay(..)number300इनलाइन कम्प्लीशन माँगे जाने से पहले टाइपिंग कितने मिलीसेकंड रुकती है।
class, styleस्टाइल मेथडstring, StyleProp<ViewStyle>एडिटर का बॉक्स।

#Events

EventRustविवरण
onChange.on_change(|value: &SharedString| ..)टेक्स्ट बदला: { value }, पूरा नया टेक्स्ट।
onSelectionChange.on_selection_change(|selections: &Vec<EditorSelection>| ..)सेलेक्शन बदले: { selections }, हर एक, प्राइमरी सबसे अंत में।
onFocus, onBlur.on_focus(|| ..), .on_blur(|| ..)एडिटर को फ़ोकस मिला या उसने फ़ोकस खोया।
onSearchChange.on_search_change(|result: &EditorSearchResult| ..)खोज के मिलान बदले: { result }, एक { count, current, error }।
onLineNumberPress.on_line_number_press(|line: &u32| ..)किसी लाइन नंबर को दबाया गया: { line }, वह लाइन, 0 से शुरू।
onContextMenu.on_context_menu(|event: &EditorContextMenuEvent| ..)टेक्स्ट के ऊपर सेकेंडरी बटन दबाया गया, या टच से टेक्स्ट चुना गया: { position, x, y, selection? }।
onHover.on_hover(|event: &EditorHoverEvent| ..)माउस किसी अक्षर पर ठहरा है (भाषा की सुविधाएँ)।
onHoverEnd.on_hover_end(|| ..)होवर समाप्त हुआ।
onTextPress.on_text_press(|event: &EditorTextPressEvent| ..)कोई अक्षर दबाया गया: उसके शब्द, मॉडिफ़ायर और क्लिक के साथ।
onFindWithSelection.on_find_with_selection(|query: &String| ..)findWithSelection चला: { query }, फ़ाइंड बार की क्वेरी के लिए टेक्स्ट।
onFoldChange.on_fold_change(|folds: &Vec<EditorFoldRange>| ..)फ़ोल्ड की गई रेंज बदलीं: { folds }, हर एक, नेस्टेड सहित।
onCompletionRequest.on_completion_request(|event: &EditorCompletionRequest| ..)एडिटर कम्प्लीशन माँगता है: { id, offset, position, query, queryStart, trigger, character? } (कंप्लीशन)।
onCompletionAccept.on_completion_accept(|event: &EditorCompletionAcceptEvent| ..)कोई आइटम स्वीकार किया गया और डाला गया: { item }।
onInlineCompletionRequest.on_inline_completion_request(|event: &EditorInlineCompletionRequest| ..)टाइपिंग रुकी: { id, offset, position }, इनलाइन कम्प्लीशन के लिए।
onDefinitionRequest.on_definition_request(|event: &EditorDefinitionRequest| ..)एडिटर डेफ़िनिशन माँगता है: { id, position, offset, range, trigger } (डेफ़िनिशन पर जाएँ)।
onCodeActionRequest.on_code_action_request(|event: &EditorCodeActionRequest| ..)उपयोगकर्ता ने कोड एक्शन माँगे: { range, position, bounds? }।

#एक्सेसिबिलिटी

स्क्रीन रीडर एडिटर को उसके accessibilityLabel से पुकारते हैं; उसका फ़ोकस एक टेक्स्ट इनपुट जैसा है, इसलिए प्लेटफ़ॉर्म की टेक्स्ट सेवाएँ (इनपुट मेथड, डिक्टेशन, सॉफ़्ट कीबोर्ड) उसमें काम करती हैं।

#संबंधित

  • TextView: Markdown और HTML के दस्तावेज़, जिनके कोड ब्लॉक एडिटर के सिंटैक्स रंग साझा करते हैं।
  • DiffView: दो टेक्स्ट के बीच के बदलाव, एडिटर के सिंटैक्स रंगों में।
  • Input: प्लेटफ़ॉर्म की अपनी एडिटिंग वाले सिंगल और मल्टीलाइन टेक्स्ट फ़ील्ड।
  • keymap: एडिटर के कमांड के लिए ऐप की कीज़।
  • nsh/base: अन्य बेस कॉम्पोनेंट।