mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	typeahead: Replace code block language hint text with more clear options
This commit - Replace the blank option with an italicized option that's the current default language, if there is one selected with "default" label. - Make the "text" option more informative by adding (no highlighting) to the label. - Remove the hint for "text". - Prioritize as left to right, before start typing: blank/default language, text, quote, spoiler, math, everything else... fixes: #33682
This commit is contained in:
		@@ -6,6 +6,7 @@ from pygments.lexers import get_all_lexers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
 | 
					ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
 | 
				
			||||||
# The current priorities data set is based on StackOverflow's 2020 survey.
 | 
					# The current priorities data set is based on StackOverflow's 2020 survey.
 | 
				
			||||||
 | 
					# We also prioritize text, quote, math, spoiler over others to enhance UX.
 | 
				
			||||||
DATA_PATH = os.path.join(ZULIP_PATH, "tools", "setup", "lang.json")
 | 
					DATA_PATH = os.path.join(ZULIP_PATH, "tools", "setup", "lang.json")
 | 
				
			||||||
OUT_PATH = os.path.join(ZULIP_PATH, "web", "generated", "pygments_data.json")
 | 
					OUT_PATH = os.path.join(ZULIP_PATH, "web", "generated", "pygments_data.json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "default": {
 | 
					    "default": {
 | 
				
			||||||
 | 
					        "text": 31,
 | 
				
			||||||
        "javascript": 27,
 | 
					        "javascript": 27,
 | 
				
			||||||
        "python": 26,
 | 
					        "python": 26,
 | 
				
			||||||
        "java": 25,
 | 
					        "java": 25,
 | 
				
			||||||
@@ -30,9 +31,9 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "custom": {
 | 
					    "custom": {
 | 
				
			||||||
        "latex": 10,
 | 
					        "latex": 10,
 | 
				
			||||||
        "math": 5,
 | 
					        "math": 28,
 | 
				
			||||||
        "quote": 5,
 | 
					        "quote": 30,
 | 
				
			||||||
        "spoiler": 5
 | 
					        "spoiler": 29
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "aliases": {
 | 
					    "aliases": {
 | 
				
			||||||
        "js": 27,
 | 
					        "js": 27,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -843,11 +843,12 @@ export function get_candidates(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        completing = "syntax";
 | 
					        completing = "syntax";
 | 
				
			||||||
        token = current_token;
 | 
					        token = current_token;
 | 
				
			||||||
        // If the code formatting button was triggered, we want to show a blank option
 | 
					
 | 
				
			||||||
        // to improve the discoverability of the possibility of specifying a language.
 | 
					        const default_language = realm.realm_default_code_block_language;
 | 
				
			||||||
        const language_list = compose_ui.code_formatting_button_triggered
 | 
					        const language_list = realm_playground.get_pygments_typeahead_list_for_composebox();
 | 
				
			||||||
            ? ["", ...realm_playground.get_pygments_typeahead_list_for_composebox()]
 | 
					        if (default_language) {
 | 
				
			||||||
            : realm_playground.get_pygments_typeahead_list_for_composebox();
 | 
					            language_list.unshift(default_language);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        compose_ui.set_code_formatting_button_triggered(false);
 | 
					        compose_ui.set_code_formatting_button_triggered(false);
 | 
				
			||||||
        const matcher = get_language_matcher(token);
 | 
					        const matcher = get_language_matcher(token);
 | 
				
			||||||
        const matches = language_list.filter((item) => matcher(item));
 | 
					        const matches = language_list.filter((item) => matcher(item));
 | 
				
			||||||
@@ -1109,7 +1110,12 @@ export function content_highlighter_html(item: TypeaheadSuggestion): string | un
 | 
				
			|||||||
        case "stream":
 | 
					        case "stream":
 | 
				
			||||||
            return typeahead_helper.render_stream(item);
 | 
					            return typeahead_helper.render_stream(item);
 | 
				
			||||||
        case "syntax":
 | 
					        case "syntax":
 | 
				
			||||||
            return typeahead_helper.render_typeahead_item({primary: item.language});
 | 
					            return typeahead_helper.render_typeahead_item({
 | 
				
			||||||
 | 
					                primary: item.language,
 | 
				
			||||||
 | 
					                is_default_language:
 | 
				
			||||||
 | 
					                    item.language !== "" &&
 | 
				
			||||||
 | 
					                    item.language === realm.realm_default_code_block_language,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        case "topic_jump":
 | 
					        case "topic_jump":
 | 
				
			||||||
            return typeahead_helper.render_typeahead_item({primary: item.message});
 | 
					            return typeahead_helper.render_typeahead_item({primary: item.message});
 | 
				
			||||||
        case "topic_list": {
 | 
					        case "topic_list": {
 | 
				
			||||||
@@ -1415,15 +1421,6 @@ function get_footer_html(): string | false {
 | 
				
			|||||||
        case "silent_mention":
 | 
					        case "silent_mention":
 | 
				
			||||||
            tip_text = $t({defaultMessage: "This silent mention won't trigger notifications."});
 | 
					            tip_text = $t({defaultMessage: "This silent mention won't trigger notifications."});
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case "syntax":
 | 
					 | 
				
			||||||
            if (realm.realm_default_code_block_language !== "") {
 | 
					 | 
				
			||||||
                tip_text = $t(
 | 
					 | 
				
			||||||
                    {defaultMessage: "Default is {language}. Use 'text' to disable highlighting."},
 | 
					 | 
				
			||||||
                    {language: realm.realm_default_code_block_language},
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1465,6 +1462,15 @@ export function initialize_compose_typeahead($element: JQuery<HTMLTextAreaElemen
 | 
				
			|||||||
                    if (item.is_new_topic) {
 | 
					                    if (item.is_new_topic) {
 | 
				
			||||||
                        return `<em>${$t({defaultMessage: "New"})}</em>`;
 | 
					                        return `<em>${$t({defaultMessage: "New"})}</em>`;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (item.type === "syntax") {
 | 
				
			||||||
 | 
					                    if (
 | 
				
			||||||
 | 
					                        item.language !== "" &&
 | 
				
			||||||
 | 
					                        item.language === realm.realm_default_code_block_language
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        return `<em>${$t({defaultMessage: "(default)"})}</em>`;
 | 
				
			||||||
 | 
					                    } else if (item.language === "text") {
 | 
				
			||||||
 | 
					                        return `<em>${$t({defaultMessage: "(no highlighting)"})}</em>`;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,6 +113,7 @@ export let render_typeahead_item = (args: {
 | 
				
			|||||||
    topic_object?: TopicSuggestion;
 | 
					    topic_object?: TopicSuggestion;
 | 
				
			||||||
    is_stream_topic?: boolean;
 | 
					    is_stream_topic?: boolean;
 | 
				
			||||||
    is_empty_string_topic?: boolean;
 | 
					    is_empty_string_topic?: boolean;
 | 
				
			||||||
 | 
					    is_default_language?: boolean;
 | 
				
			||||||
}): string => {
 | 
					}): string => {
 | 
				
			||||||
    const has_image = args.img_src !== undefined;
 | 
					    const has_image = args.img_src !== undefined;
 | 
				
			||||||
    const has_status = args.status_emoji_info !== undefined;
 | 
					    const has_status = args.status_emoji_info !== undefined;
 | 
				
			||||||
@@ -434,8 +435,15 @@ function retain_unique_language_aliases(matches: string[]): string[] {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export function sort_languages(matches: LanguageSuggestion[], query: string): LanguageSuggestion[] {
 | 
					export function sort_languages(matches: LanguageSuggestion[], query: string): LanguageSuggestion[] {
 | 
				
			||||||
    const languages = matches.map((object) => object.language);
 | 
					    const languages = matches.map((object) => object.language);
 | 
				
			||||||
 | 
					    const default_language = realm.realm_default_code_block_language;
 | 
				
			||||||
    const results = typeahead.triage(query, languages, (x) => x, compare_language);
 | 
					    const results = typeahead.triage(query, languages, (x) => x, compare_language);
 | 
				
			||||||
    const unique_languages = retain_unique_language_aliases([...results.matches, ...results.rest]);
 | 
					    let language_results;
 | 
				
			||||||
 | 
					    if (default_language && results.matches.includes(default_language)) {
 | 
				
			||||||
 | 
					        language_results = [default_language, ...results.matches, ...results.rest];
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        language_results = [...results.matches, ...results.rest];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const unique_languages = retain_unique_language_aliases(language_results);
 | 
				
			||||||
    return unique_languages.map((language) => ({
 | 
					    return unique_languages.map((language) => ({
 | 
				
			||||||
        language,
 | 
					        language,
 | 
				
			||||||
        type: "syntax",
 | 
					        type: "syntax",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2349,6 +2349,7 @@ body:not(.spectator-view) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.default-language-display,
 | 
				
			||||||
.empty-topic-display,
 | 
					.empty-topic-display,
 | 
				
			||||||
.empty-topic-placeholder-display::placeholder {
 | 
					.empty-topic-placeholder-display::placeholder {
 | 
				
			||||||
    font-style: italic;
 | 
					    font-style: italic;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@
 | 
				
			|||||||
{{else}}
 | 
					{{else}}
 | 
				
			||||||
{{!-- Separate container to ensure overflowing text remains in this container. --}}
 | 
					{{!-- Separate container to ensure overflowing text remains in this container. --}}
 | 
				
			||||||
<div class="typeahead-text-container{{#if has_secondary_html}} has_secondary_html{{/if}}">
 | 
					<div class="typeahead-text-container{{#if has_secondary_html}} has_secondary_html{{/if}}">
 | 
				
			||||||
    <strong class="typeahead-strong-section{{#if is_empty_string_topic}} empty-topic-display{{/if}}">
 | 
					    <strong class="typeahead-strong-section{{#if is_empty_string_topic}} empty-topic-display{{/if}}{{#if is_default_language}} default-language-display{{/if}}">
 | 
				
			||||||
        {{~#if stream~}}
 | 
					        {{~#if stream~}}
 | 
				
			||||||
            {{~> inline_decorated_channel_name stream=stream ~}}
 | 
					            {{~> inline_decorated_channel_name stream=stream ~}}
 | 
				
			||||||
            {{~else~}}
 | 
					            {{~else~}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2306,7 +2306,10 @@ test("content_highlighter_html", ({override_rewire}) => {
 | 
				
			|||||||
    ct.get_or_set_completing_for_tests("syntax");
 | 
					    ct.get_or_set_completing_for_tests("syntax");
 | 
				
			||||||
    th_render_typeahead_item_called = false;
 | 
					    th_render_typeahead_item_called = false;
 | 
				
			||||||
    override_rewire(typeahead_helper, "render_typeahead_item", (item) => {
 | 
					    override_rewire(typeahead_helper, "render_typeahead_item", (item) => {
 | 
				
			||||||
        assert.deepEqual(item, {primary: "py"});
 | 
					        assert.deepEqual(item, {
 | 
				
			||||||
 | 
					            is_default_language: false,
 | 
				
			||||||
 | 
					            primary: "py",
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        th_render_typeahead_item_called = true;
 | 
					        th_render_typeahead_item_called = true;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    ct.content_highlighter_html({type: "syntax", language: "py"});
 | 
					    ct.content_highlighter_html({type: "syntax", language: "py"});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,6 +78,10 @@ run_test("get_pygments_typeahead_list_for_settings", () => {
 | 
				
			|||||||
    let iterator = candidates.entries();
 | 
					    let iterator = candidates.entries();
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "Text only (text, text)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "quote (quote, quote)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "spoiler (spoiler, spoiler)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "math (math, math)");
 | 
				
			||||||
    assert.equal(iterator.next().value[1], "JavaScript (javascript, js, javascript, js)");
 | 
					    assert.equal(iterator.next().value[1], "JavaScript (javascript, js, javascript, js)");
 | 
				
			||||||
    assert.equal(
 | 
					    assert.equal(
 | 
				
			||||||
        iterator.next().value[1],
 | 
					        iterator.next().value[1],
 | 
				
			||||||
@@ -96,6 +100,10 @@ run_test("get_pygments_typeahead_list_for_settings", () => {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "Text only (text, text)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "quote (quote, quote)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "spoiler (spoiler, spoiler)");
 | 
				
			||||||
 | 
					    assert.equal(iterator.next().value[1], "math (math, math)");
 | 
				
			||||||
    assert.equal(iterator.next().value[1], "JavaScript (javascript, js, javascript, js)");
 | 
					    assert.equal(iterator.next().value[1], "JavaScript (javascript, js, javascript, js)");
 | 
				
			||||||
    assert.equal(
 | 
					    assert.equal(
 | 
				
			||||||
        iterator.next().value[1],
 | 
					        iterator.next().value[1],
 | 
				
			||||||
@@ -107,9 +115,7 @@ run_test("get_pygments_typeahead_list_for_settings", () => {
 | 
				
			|||||||
    iterator = candidates.entries();
 | 
					    iterator = candidates.entries();
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: invent_a_lang"}));
 | 
				
			||||||
    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
					    assert.equal(iterator.next().value[1], $t({defaultMessage: "Custom language: custom_lang"}));
 | 
				
			||||||
    assert.equal(iterator.next().value[1], "JavaScript (javascript, js, javascript, js)");
 | 
					    assert.equal(iterator.next().value[1], "Text only (text, text)");
 | 
				
			||||||
    assert.equal(
 | 
					    assert.equal(iterator.next().value[1], "quote (quote, quote)");
 | 
				
			||||||
        iterator.next().value[1],
 | 
					    assert.equal(iterator.next().value[1], "spoiler (spoiler, spoiler)");
 | 
				
			||||||
        "Python (python, bazel, py, py3, pyi, python3, sage, starlark, python, bazel, py, py3, pyi, python3, sage, starlark)",
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -394,12 +394,15 @@ test("sort_streams", ({override, override_rewire}) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function language_items(languages) {
 | 
					function language_items(languages) {
 | 
				
			||||||
    return languages.map((language) => ({
 | 
					    return languages.map((language) => ({
 | 
				
			||||||
        type: "syntax",
 | 
					 | 
				
			||||||
        language,
 | 
					        language,
 | 
				
			||||||
 | 
					        type: "syntax",
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test("sort_languages", ({override_rewire}) => {
 | 
					test("sort_languages", ({override, override_rewire}) => {
 | 
				
			||||||
 | 
					    override(realm, "realm_default_code_block_language", "dart");
 | 
				
			||||||
 | 
					    const default_language = realm.realm_default_code_block_language;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override_rewire(pygments_data, "langs", {
 | 
					    override_rewire(pygments_data, "langs", {
 | 
				
			||||||
        python: {priority: 26},
 | 
					        python: {priority: 26},
 | 
				
			||||||
        javascript: {priority: 27},
 | 
					        javascript: {priority: 27},
 | 
				
			||||||
@@ -407,20 +410,52 @@ test("sort_languages", ({override_rewire}) => {
 | 
				
			|||||||
        pascal: {priority: 15},
 | 
					        pascal: {priority: 15},
 | 
				
			||||||
        perl: {priority: 3},
 | 
					        perl: {priority: 3},
 | 
				
			||||||
        css: {priority: 21},
 | 
					        css: {priority: 21},
 | 
				
			||||||
 | 
					        spoiler: {priority: 29},
 | 
				
			||||||
 | 
					        text: {priority: 31},
 | 
				
			||||||
 | 
					        quote: {priority: 30},
 | 
				
			||||||
 | 
					        math: {priority: 28},
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let test_langs = language_items(["pascal", "perl", "php", "python", "javascript"]);
 | 
					    let test_langs = language_items(["pascal", "perl", "php", "python", "spoiler", "javascript"]);
 | 
				
			||||||
    test_langs = th.sort_languages(test_langs, "p");
 | 
					    test_langs = th.sort_languages(test_langs, "p");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Sort languages by matching first letter, and then by popularity
 | 
					    // Sort languages by matching first letter, and then by popularity
 | 
				
			||||||
    assert.deepEqual(test_langs, language_items(["python", "php", "pascal", "perl", "javascript"]));
 | 
					    assert.deepEqual(
 | 
				
			||||||
 | 
					        test_langs,
 | 
				
			||||||
 | 
					        language_items(["python", "php", "pascal", "perl", "spoiler", "javascript"]),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Test if popularity between two languages are the same
 | 
					    // Test if popularity between two languages are the same
 | 
				
			||||||
    pygments_data.langs.php = {priority: 26};
 | 
					    pygments_data.langs.php = {priority: 26};
 | 
				
			||||||
    test_langs = language_items(["pascal", "perl", "php", "python", "javascript"]);
 | 
					    test_langs = language_items(["pascal", "perl", "php", "python", "spoiler", "javascript"]);
 | 
				
			||||||
    test_langs = th.sort_languages(test_langs, "p");
 | 
					    test_langs = th.sort_languages(test_langs, "p");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert.deepEqual(test_langs, language_items(["php", "python", "pascal", "perl", "javascript"]));
 | 
					    assert.deepEqual(
 | 
				
			||||||
 | 
					        test_langs,
 | 
				
			||||||
 | 
					        language_items(["php", "python", "pascal", "perl", "spoiler", "javascript"]),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test_langs = language_items([
 | 
				
			||||||
 | 
					        default_language,
 | 
				
			||||||
 | 
					        "text",
 | 
				
			||||||
 | 
					        "quote",
 | 
				
			||||||
 | 
					        "math",
 | 
				
			||||||
 | 
					        "python",
 | 
				
			||||||
 | 
					        "javascript",
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					    const test_langs_for_default = th.sort_languages(test_langs, "d");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.deepEqual(
 | 
				
			||||||
 | 
					        test_langs_for_default,
 | 
				
			||||||
 | 
					        language_items([default_language, "text", "quote", "math", "javascript", "python"]),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test_langs = th.sort_languages(test_langs, "t");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.deepEqual(
 | 
				
			||||||
 | 
					        test_langs,
 | 
				
			||||||
 | 
					        language_items(["text", "quote", "math", "javascript", "python", default_language]),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test("sort_languages on actual data", () => {
 | 
					test("sort_languages on actual data", () => {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user