Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Communication: Fix an issue with list formatting in Markdown #9925

Merged
merged 4 commits into from
Dec 3, 2024

Conversation

asliayk
Copy link
Contributor

@asliayk asliayk commented Dec 2, 2024

Checklist

General

Client

Motivation and Context

  • Previously, the editor automatically added list prefixes (e.g., -, 1.) after selecting the list option. However, it did not automatically add a prefix when the previous line already contained one unless the user had pressed the list option button once before starting the list, unlike the behavior in Slack.
  • The unordered list prefix character is incompatible with the iOS app, it needs to be changed from "• " to "- ".
    (Closes Communication: Invalid Markdown for Lists in Artemis #9881)

Description

  • This change implements auto-prefixing for list creation. If the previous line contains a list prefix (e.g., -, 1.), the editor now automatically adds the same prefix to the next line, without requiring the user to manually select the list option.
  • The unordered list prefix character has been changed from "• " to "- ".

Steps for Testing

Prerequisites:

  • 1 Instructor/Student
  • 1 Course with Communication enabled
  1. Log in to Artemis.
  2. Navigate to the Communication section of a course.
  3. Without selecting any list option, type something that starts with a list prefix, such as "- list item" or "1. list item."
  4. Press Shift/Cmd + Enter to move to a new line and notice that the prefix is added automatically.

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

Client

Class/File Line Coverage Confirmation (assert/expect)
posting-content-part.components.ts 94.11% ✅ ❌
posting-markdown-editor.component.ts 82.5% ✅ ❌
bulleted-list.action.ts 100%
list.action.ts 76.92% ✅ ❌
ordered-list.action.ts 100%
monaco-editor.component.ts 94.8% ✅ ❌

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced content processing to support unordered lists in the editor.
    • Improved markdown editor functionality for dynamic list formatting based on user input.
    • New methods added to the Monaco editor component for better content management.
  • Bug Fixes

    • Adjusted list prefix handling to ensure consistent representation in the text editor.
  • Tests

    • Expanded test coverage for content processing, markdown editing, and Monaco editor interactions to ensure robustness and accuracy.
    • Added tests for handling model content changes and retrieving line content in the Monaco editor.

@asliayk asliayk self-assigned this Dec 2, 2024
@asliayk asliayk requested a review from a team as a code owner December 2, 2024 01:38
@github-actions github-actions bot added tests client Pull requests that update TypeScript code. (Added Automatically!) labels Dec 2, 2024
Copy link

coderabbitai bot commented Dec 2, 2024

Walkthrough

The pull request introduces several modifications across multiple components related to content processing in a markdown editor. Key changes include the addition of a method to escape unordered list items, updates to existing methods for handling numbered lists, and enhancements to the markdown editor's event handling for user input. Additionally, the representation of bullet points is altered, and new public methods are introduced in the Monaco editor component to improve interaction capabilities. The changes are accompanied by new test cases to ensure proper functionality across the updated features.

Changes

File Change Summary
src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts Added escapeUnorderedList method; updated processContent to call this new method. Minor adjustment to escapeNumberedList.
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts Added handleKeyDown method; updated ngAfterViewInit to listen for changes in the Monaco editor.
src/main/webapp/app/shared/monaco-editor/model/actions/bulleted-list.action.ts Changed BULLET_PREFIX from '• ' to '- '.
src/main/webapp/app/shared/monaco-editor/model/actions/list.action.ts Updated regex patterns for list prefixes; simplified logic for prefix management.
src/main/webapp/app/shared/monaco-editor/model/actions/ordered-list.action.ts Simplified getPrefix method to consistently return a single space character.
src/main/webapp/app/shared/monaco-editor/monaco-editor.component.ts Added methods: onDidChangeModelContent, getModel, getLineContent.
src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts Added test suite for content processing; included tests for processContent, escapeNumberedList, and escapeUnorderedList.
src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts Updated tests to reflect changes in list handling and added tests for key down events.
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts Added tests for new methods in MonacoEditorComponent related to model content changes.

Possibly related PRs

Suggested labels

ready to merge, small, component:Communication

Suggested reviewers

  • HawKhiem
  • florian-glombik
  • sachmii
  • SimonEntholzer
  • Malekos74
  • Feras797

Warning

Rate limit exceeded

@asliayk has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ea10516 and 34c27a0.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (19)
src/main/webapp/app/shared/monaco-editor/model/actions/ordered-list.action.ts (2)

15-16: Optimize and align with style guidelines

The implementation can be improved for better maintainability and style compliance:

-        const space = ' ';
-        return `${lineNumber}.${space}`;
+    private static readonly SPACE = ' ';
+    return lineNumber + '.' + OrderedListAction.SPACE;

Changes:

  • Move space constant to class level as it's static
  • Use 4-space indentation per guidelines
  • Use single quotes instead of template literals per guidelines
  • Use string concatenation for better readability

Line range hint 19-19: Initialize the required PREFIX property

The PREFIX property is declared but not initialized. This could lead to undefined behavior if the base class ListAction attempts to use this property.

-    protected readonly PREFIX: string;
+    protected readonly PREFIX = '1. ';
src/main/webapp/app/shared/monaco-editor/model/actions/bulleted-list.action.ts (1)

Line range hint 7-9: Consider enhancing the class documentation.

While the current documentation describes the basic purpose, it could be more detailed to reflect the automatic list continuation behavior.

 /**
  * Action used to add or modify a bullet-point list in the text editor.
+ * The list items use the markdown-style hyphen prefix ('- ') and support
+ * automatic continuation when pressing Enter at the end of a list item.
  */
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (2)

293-300: Enhance assertion specificity in the model content changes test

The test correctly verifies the listener registration, but could be more specific in its assertion.

Consider updating the assertion to verify the exact parameters passed to the listener:

-        expect(listenerStub).toHaveBeenCalled();
+        expect(listenerStub).toHaveBeenCalledExactlyOnceWith(expect.any(Object));

302-308: Consider adding edge case coverage for model retrieval

While the test covers the basic functionality, consider adding edge cases.

Add test cases for:

  1. Model retrieval before setting text
  2. Model retrieval after model disposal
  3. Model retrieval with empty text

Example:

it('should handle edge cases in model retrieval', () => {
    fixture.detectChanges();
    // Before setting text
    expect(comp.getModel()).not.toBeNull();
    expect(comp.getModel()?.getValue()).toBe('');
    
    // After disposal
    const model = comp.getModel();
    model?.dispose();
    expect(comp.getModel()).toBeNull();
});
src/main/webapp/app/shared/monaco-editor/monaco-editor.component.ts (2)

125-127: Add return type and documentation

The method implementation is correct but could be improved with explicit typing and documentation.

-    public getModel() {
+    /**
+     * Returns the current text model of the editor.
+     * @returns The editor's text model or null if no model is set.
+     */
+    public getModel(): monaco.editor.ITextModel | null {
         return this._editor.getModel();
     }

129-132: Add parameter validation and documentation

The method handles null models well but could be improved with input validation and documentation.

-    public getLineContent(lineNumber: number): string {
+    /**
+     * Returns the content of a specific line in the editor.
+     * @param lineNumber The 1-based line number to get content from
+     * @returns The content of the specified line or empty string if line doesn't exist
+     * @throws {Error} If lineNumber is less than 1
+     */
+    public getLineContent(lineNumber: number): string {
+        if (lineNumber < 1) {
+            throw new Error('Line number must be greater than 0');
+        }
         const model = this._editor.getModel();
         return model ? model.getLineContent(lineNumber) : '';
     }
src/main/webapp/app/shared/monaco-editor/model/actions/list.action.ts (3)

96-100: Consider trimming the line before prefix check

While the prefix handling logic is correct, consider trimming whitespace before checking the prefix to handle cases where the line might have leading spaces.

-if (currentLineText.startsWith(newPrefix)) {
+if (currentLineText.trim().startsWith(newPrefix.trim())) {

Line range hint 116-130: Refactor prefix type determination

The current implementation uses magic strings and direct comparisons which could be fragile. Consider introducing constants and a more robust type checking mechanism.

+private static readonly BULLET_PREFIX = '- ';
+private static readonly NUMBERED_PREFIX_REGEX = /^\s*\d+\.\s+/;

-if (this.getPrefix(1) != '- ') {
+if (!this.getPrefix(1).startsWith(ListAction.BULLET_PREFIX)) {
-    const numberedListRegex = /^\s*\d+\.\s+/;
-    allLinesHaveCurrentPrefix = lines.every((line) => numberedListRegex.test(line));
+    allLinesHaveCurrentPrefix = lines.every((line) => 
+        ListAction.NUMBERED_PREFIX_REGEX.test(line));

Line range hint 52-90: Consider adding event cleanup mechanism

The current implementation adds event listeners but doesn't provide a mechanism to remove them when the editor is destroyed. This could potentially lead to memory leaks.

Consider adding a cleanup method:

protected cleanup(editor: TextEditor): void {
    const domNode = editor.getDomNode();
    if (domNode) {
        domNode.removeEventListener('keydown', this.handleKeyDown);
    }
    ListAction.editorsWithListener.delete(editor);
}
src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (2)

40-40: Consider consolidating mock setup.

The mock setup for the Monaco editor is spread across multiple locations. Consider consolidating all Monaco-related mock setup into a single location, preferably in the beforeEach block, to improve maintainability.

 beforeEach(() => {
     // ... existing setup ...
     mockMarkdownEditorComponent = fixture.debugElement.query(By.directive(MarkdownEditorMonacoComponent)).componentInstance;
-    mockMarkdownEditorComponent.monacoEditor.getPosition = jest.fn();
-    mockMarkdownEditorComponent.monacoEditor.getModel = jest.fn();
+    // Consolidate Monaco editor mock setup
+    mockMarkdownEditorComponent.monacoEditor = {
+        ...mockMarkdownEditorComponent.monacoEditor,
+        getPosition: jest.fn(),
+        getModel: jest.fn(),
+    };

Also applies to: 128-128, 145-146


444-444: Extract repeated test data into constants.

There are multiple occurrences of the same test data strings across different test cases. Consider extracting these into constants to improve maintainability and reduce duplication.

+const TEST_DATA = {
+    PLAIN_TEXT: 'First line\nSecond line\nThird line',
+    BULLETED_LIST: '- First line\n- Second line\n- Third line',
+    ORDERED_LIST: '1. First line\n2. Second line\n3. Third line',
+};

 it('should add bulleted list prefixes correctly', () => {
     const bulletedListAction = component.defaultActions.find((action: any) => action instanceof BulletedListAction) as BulletedListAction;
-    const selectedText = `First line\nSecond line\nThird line`;
-    const expectedText = `- First line\n- Second line\n- Third line`;
+    const selectedText = TEST_DATA.PLAIN_TEXT;
+    const expectedText = TEST_DATA.BULLETED_LIST;

Also applies to: 451-451, 460-460, 476-477, 489-489, 500-500, 509-510, 524-524, 530-530, 544-544, 547-547

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (2)

92-93: Consider supporting additional unordered list markers

The current implementation only handles hyphen-prefixed lists (-). Consider extending support for other common markdown list markers like asterisk (*) and plus (+).

 escapeUnorderedList(content: string): string {
-    return content.replace(/^(- )/gm, '\\$1');
+    return content.replace(/^([*+-] )/gm, '\\$1');
 }

79-93: Consider extracting markdown processing logic

The component currently handles both UI responsibilities and markdown processing logic. Consider extracting the list processing methods into a separate markdown service for better separation of concerns and reusability.

This would:

  1. Make the markdown processing logic more reusable across components
  2. Simplify testing of the processing logic
  3. Keep the component focused on its primary UI responsibilities
src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts (4)

275-291: Consider improving test readability and maintainability.

While the test is comprehensive, consider these improvements:

  1. Make the test name more descriptive, e.g., "should correctly escape both numbered and unordered lists in content before and after references"
  2. Extract test data into variables with meaningful names for better readability
-        it('should process content before and after reference with escaped numbered and unordered lists', () => {
+        it('should correctly escape both numbered and unordered lists in content before and after references', () => {
+            const numberedListContent = '1. This is a numbered list\n2. Another item';
+            const unorderedListContent = '- This is an unordered list';
+            const contentBefore = `${numberedListContent}\n${unorderedListContent}`;
             
-            const contentBefore = '1. This is a numbered list\n2. Another item\n- This is an unordered list';
-            const contentAfter = '1. Numbered again\n- Unordered again';
+            const contentAfter = '1. Numbered again\n- Unordered again';

293-297: Consider adding edge cases for numbered list escaping.

The test covers the basic case well, but consider adding test cases for:

  • Single-digit vs multi-digit numbers (e.g., "10. ", "100. ")
  • Different spacing after numbers
  • Invalid number formats
         it('should escape numbered lists correctly', () => {
-            const content = '1. First item\n2. Second item\n3. Third item';
-            const escapedContent = component.escapeNumberedList(content);
-            expect(escapedContent).toBe('1\\.  First item\n2\\.  Second item\n3\\.  Third item');
+            const testCases = [
+                {
+                    input: '1. First item\n2. Second item\n3. Third item',
+                    expected: '1\\.  First item\n2\\.  Second item\n3\\.  Third item'
+                },
+                {
+                    input: '10. Tenth item\n100. Hundredth item',
+                    expected: '10\\.  Tenth item\n100\\.  Hundredth item'
+                },
+                {
+                    input: '1.First item\n2.  Second item',
+                    expected: '1\\. First item\n2\\.  Second item'
+                }
+            ];
+            
+            testCases.forEach(({ input, expected }) => {
+                expect(component.escapeNumberedList(input)).toBe(expected);
+            });
         });

299-303: Consider testing different unordered list markers.

The test covers the basic case with "-" markers, but consider adding test cases for:

  • Different list markers (* and +)
  • Mixed markers in the same content
  • Different spacing after markers
         it('should escape unordered lists correctly', () => {
-            const content = '- First item\n- Second item\n- Third item';
-            const escapedContent = component.escapeUnorderedList(content);
-            expect(escapedContent).toBe('\\- First item\n\\- Second item\n\\- Third item');
+            const testCases = [
+                {
+                    input: '- First item\n- Second item',
+                    expected: '\\- First item\n\\- Second item'
+                },
+                {
+                    input: '* First item\n+ Second item',
+                    expected: '\\* First item\n\\+ Second item'
+                },
+                {
+                    input: '-First item\n-  Second item',
+                    expected: '\\-First item\n\\-  Second item'
+                }
+            ];
+            
+            testCases.forEach(({ input, expected }) => {
+                expect(component.escapeUnorderedList(input)).toBe(expected);
+            });
         });

313-317: Consider verifying order independence of escaping operations.

The test verifies one order of operations (unordered then numbered), but it would be valuable to verify that the order of escaping operations doesn't affect the result.

         it('should handle mixed numbered and unordered lists in content', () => {
             const content = '1. Numbered item\n- Unordered item\n2. Another numbered item\n- Another unordered item';
-            const escapedContent = component.escapeNumberedList(component.escapeUnorderedList(content));
-            expect(escapedContent).toBe('1\\.  Numbered item\n\\- Unordered item\n2\\.  Another numbered item\n\\- Another unordered item');
+            const expected = '1\\.  Numbered item\n\\- Unordered item\n2\\.  Another numbered item\n\\- Another unordered item';
+            
+            // Verify order doesn't matter
+            const unorderedThenNumbered = component.escapeNumberedList(component.escapeUnorderedList(content));
+            const numberedThenUnordered = component.escapeUnorderedList(component.escapeNumberedList(content));
+            
+            expect(unorderedThenNumbered).toBe(expected);
+            expect(numberedThenUnordered).toBe(expected);
         });
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

153-158: Handle potential null values when finding actions

In the handleKeyDown method, the code uses the non-null assertion operator ! after find, assuming that the action will always be found in this.defaultActions. If the action is not found, this could lead to a runtime error. Consider adding a null check to ensure the code is robust.

Apply this change:

- this.markdownEditor.handleActionClick(new MouseEvent('click'), this.defaultActions.find((action) => action instanceof BulletedListAction)!);
+ const bulletedListAction = this.defaultActions.find((action) => action instanceof BulletedListAction);
+ if (bulletedListAction) {
+     this.markdownEditor.handleActionClick(new MouseEvent('click'), bulletedListAction);
+ }

Similarly, update for the OrderedListAction:

- this.markdownEditor.handleActionClick(new MouseEvent('click'), this.defaultActions.find((action) => action instanceof OrderedListAction)!);
+ const orderedListAction = this.defaultActions.find((action) => action instanceof OrderedListAction);
+ if (orderedListAction) {
+     this.markdownEditor.handleActionClick(new MouseEvent('click'), orderedListAction);
+ }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between dece904 and 1cb77e8.

📒 Files selected for processing (9)
  • src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2 hunks)
  • src/main/webapp/app/shared/monaco-editor/model/actions/bulleted-list.action.ts (1 hunks)
  • src/main/webapp/app/shared/monaco-editor/model/actions/list.action.ts (7 hunks)
  • src/main/webapp/app/shared/monaco-editor/model/actions/ordered-list.action.ts (1 hunks)
  • src/main/webapp/app/shared/monaco-editor/monaco-editor.component.ts (1 hunks)
  • src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts (1 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (13 hunks)
  • src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
src/main/webapp/app/shared/monaco-editor/model/actions/ordered-list.action.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/monaco-editor/model/actions/bulleted-list.action.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/main/webapp/app/shared/monaco-editor/monaco-editor.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/monaco-editor/model/actions/list.action.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

🔇 Additional comments (11)
src/main/webapp/app/shared/monaco-editor/model/actions/bulleted-list.action.ts (2)

4-4: LGTM! The change to hyphen-based bullet points aligns with standard markdown syntax.

The switch from bullet character ('• ') to hyphen ('- ') makes the editor more compatible with standard markdown and matches common editor behaviors (like Slack). This change effectively supports the PR's goal of automatic list continuation.


Line range hint 4-24: Verify integration with related components.

The change to hyphen-based bullets needs to work seamlessly with the new escapeUnorderedList method in PostingContentPartComponent and the list prefix handling in PostingMarkdownEditorComponent.

✅ Verification successful

Based on the shell script results, I can now generate the final response as the verification is complete. The results show consistent integration across components:

Integration verified - hyphen-based bullets are consistently implemented

The codebase shows proper integration with:

  • PostingContentPartComponent correctly escapes unordered lists with hyphens via escapeUnorderedList method
  • PostingMarkdownEditorComponent properly handles hyphen prefixes in its event handling
  • The BULLET_PREFIX constant with value '- ' is consistently used across list-related actions
  • Test coverage confirms the proper escaping and handling of hyphen-based bullets
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the consistent usage of the new hyphen prefix across related components

# Check for list prefix handling in the editor components
echo "Checking for list prefix handling implementations..."
rg -A 5 "escapeUnorderedList|handleListPrefix" --type ts

# Verify the usage of hyphen prefix across the codebase
echo "Checking for bullet point prefix usage..."
rg -A 2 "BULLET_PREFIX|'- '|\"- \"" --type ts

Length of output: 9927

src/main/webapp/app/shared/monaco-editor/monaco-editor.component.ts (2)

121-123: LGTM! Clean implementation of content change listener

The method properly exposes Monaco's content change event while maintaining proper cleanup through IDisposable return type.


121-132: Consider adding unit tests for new methods

While the implementation looks solid, it would be beneficial to add unit tests for these new methods to ensure they handle edge cases correctly (e.g., invalid line numbers, null models, etc.).

src/main/webapp/app/shared/monaco-editor/model/actions/list.action.ts (2)

40-40: LGTM: Consistent regex patterns for standard markdown list prefixes

The simplified regex patterns correctly match standard markdown list prefixes (-, *, +) and maintain consistency across methods.

Also applies to: 147-147


193-193: LGTM: Consistent prefix detection in backspace handling

The regex pattern correctly matches both numbered and bullet list prefixes, maintaining consistency with other patterns in the file.

src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (1)

Line range hint 1-570: Overall test implementation looks good!

The test suite provides comprehensive coverage of the list functionality and key event handling. The implementation follows testing best practices and includes proper mocking and setup.

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (2)

79-79: LGTM: Consistent processing of list formatting

The changes correctly apply unordered list escaping after numbered list processing for both content segments, maintaining consistency.

Also applies to: 84-84


89-90: Verify the impact of double spacing after numbered list markers

The change adds an extra space after escaped numbered list markers. Please verify this doesn't affect the rendered output or user experience negatively.

✅ Verification successful

The previous search was too specific. Let's broaden it to find relevant test files and usages of the method to understand its testing coverage and impact.


Double spacing after numbered list markers is expected and verified

The test cases in posting-content-part.component.spec.ts explicitly verify that the double spacing after escaped numbered list markers is the expected behavior:

  • Test case should escape numbered lists correctly verifies the exact output format with two spaces: '1\\. First item'
  • Test case should handle mixed numbered and unordered lists confirms the same spacing pattern
  • The behavior is consistently tested across multiple scenarios including mixed content
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests that verify list rendering
rg -l "escapeNumberedList.*test" --type ts

Length of output: 44


Script:

#!/bin/bash
# Search for test files related to posting content
fd "posting.*test" --type f --extension ts

# Search for any usage of escapeNumberedList
rg "escapeNumberedList" --type ts -A 2 -B 2

# Search for any test cases involving numbered lists
rg "numbered.*list" --type ts -i

Length of output: 8733

src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts (2)

274-274: LGTM! Well-organized test suite structure.

The test suite follows Jest best practices with clear organization and descriptive naming.


305-311: LGTM! Good edge case coverage.

The test effectively verifies that non-list content remains unchanged when passed through both escaping methods.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb77e8 and ea10516.

📒 Files selected for processing (2)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (12 hunks)
  • src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-action.integration.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts
🧰 Additional context used
📓 Path-based instructions (1)
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-action.integration.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

🔇 Additional comments (1)
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-action.integration.spec.ts (1)

141-141: LGTM! Standardizing list prefix spacing.

The change from two spaces to one space after the ordered list prefix improves consistency with common markdown editors.

Let's verify that this spacing convention is consistently applied across the codebase:

✅ Verification successful

The search results show that the list prefix spacing is consistent across the codebase. The matches found in artemis-date.pipe.spec.ts are actually for date formatting in German locale (where dates are formatted like "14. Apr. 2020"), not for list prefixes. The only actual list-related assertions are in monaco-editor-action.integration.spec.ts, and they both use single space after the prefix ('- ' and '1. ').

List prefix spacing is consistently implemented

The change from two spaces to one space aligns with the existing implementation, as verified by the codebase search. All list-related assertions use single space after both ordered ('1. ') and unordered ('- ') list prefixes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent list prefix spacing across the codebase
# Expected: All list prefixes should be followed by a single space

# Search for ordered list patterns with multiple spaces
rg "^\d+\.\s{2,}" --type ts

# Search for unordered list patterns with multiple spaces
rg "^[-*+]\s{2,}" --type ts

# Search for list-related test assertions to ensure consistent spacing expectations
rg "expect\(.+\).toBe\(['\"](\d+\.|[-*+])\s+" --type ts

Length of output: 899

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 2, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (3)

293-300: Enhance listener test assertions

While the test verifies that the listener is called, it should also verify the content of the change event to ensure the listener receives the correct data.

 it('should register a listener for model content changes', () => {
     const listenerStub = jest.fn();
     fixture.detectChanges();
     const disposable = comp.onDidChangeModelContent(listenerStub);
     comp.setText(singleLineText);
-    expect(listenerStub).toHaveBeenCalled();
+    expect(listenerStub).toHaveBeenCalledOnce();
+    const event = listenerStub.mock.calls[0][0];
+    expect(event.changes).toBeDefined();
+    expect(event.changes.length).toBeGreaterThan(0);
     disposable.dispose();
 });

302-308: Add test case for empty model

The test should also verify the behavior when no text has been set in the editor.

 it('should retrieve the editor model', () => {
     fixture.detectChanges();
+    // Initial empty state
+    const emptyModel = comp.getModel();
+    expect(emptyModel).not.toBeNull();
+    expect(emptyModel?.getValue()).toBe('');
+
+    // After setting text
     comp.setText(singleLineText);
     const model = comp.getModel();
     expect(model).not.toBeNull();
     expect(model?.getValue()).toBe(singleLineText);
 });

310-315: Add boundary test cases for line content retrieval

The test should also verify the behavior for first and last lines of the text to ensure proper handling of boundary cases.

 it('should get the content of a specific line', () => {
     fixture.detectChanges();
     comp.setText(multiLineText);
+    // First line
+    expect(comp.getLineContent(1)).toBe('public class Main {');
+    // Middle line
     const lineContent = comp.getLineContent(2);
     expect(lineContent).toBe('static void main() {');
+    // Last line
+    expect(comp.getLineContent(5)).toBe('}');
 });
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

126-160: Consider extracting list handling logic into a separate service

While the implementation achieves the auto-list prefixing objective, the current approach mixes concerns within the component. Consider:

  1. Creating a dedicated ListHandlingService to encapsulate list-related logic
  2. Using RxJS for handling editor events
  3. Implementing proper cleanup using takeUntil pattern

This would improve:

  • Testability: List handling logic can be tested in isolation
  • Maintainability: Easier to add new list types or modify existing behavior
  • Reusability: List handling could be used in other editor components
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ea10516 and 34c27a0.

📒 Files selected for processing (3)
  • src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (12 hunks)
  • src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

🔇 Additional comments (3)
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor.component.spec.ts (1)

317-329: LGTM! Comprehensive error case coverage

The test thoroughly covers invalid line numbers and empty line scenarios as suggested in the previous review. Good job implementing the feedback!

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2)

18-18: LGTM: Monaco editor import added correctly

The import follows Angular style guidelines and is necessary for the new functionality.


129-149: ⚠️ Potential issue

Memory leak prevention needed

The event listener needs to be properly disposed when the component is destroyed.

Apply this change:

+ private modelContentChangeListener?: monaco.IDisposable;

  ngAfterViewInit(): void {
    this.markdownEditor.enableTextFieldMode();
    const editor = this.markdownEditor.monacoEditor;
    if (editor) {
-     editor.onDidChangeModelContent((event: monaco.editor.IModelContentChangedEvent) => {
+     this.modelContentChangeListener = editor.onDidChangeModelContent((event: monaco.editor.IModelContentChangedEvent) => {
        // ... existing code ...
      });
    }
  }

+ ngOnDestroy(): void {
+   this.modelContentChangeListener?.dispose();
+ }

Copy link

@HawKhiem HawKhiem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on TS3. Works as described

image

image

Copy link

github-actions bot commented Dec 2, 2024

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test2.artemis.cit.tum.de" is already in use by PR #9927.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Dec 2, 2024
@Haoyuli2002 Haoyuli2002 added deploy:artemis-test3 and removed deployment-error Added by deployment workflows if an error occured labels Dec 2, 2024
Copy link

github-actions bot commented Dec 2, 2024

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test3.artemis.cit.tum.de" is already in use by PR #9558.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Dec 2, 2024
Copy link

@Haoyuli2002 Haoyuli2002 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on TS2 as Instructor 16. I checked list options including "-" and "1", "2". Both of them are woking as described.
Screenshot 2024-12-02 at 11 56 04

@zagemello zagemello added deploy:artemis-test2 and removed deployment-error Added by deployment workflows if an error occured labels Dec 2, 2024
@zagemello zagemello temporarily deployed to artemis-test2.artemis.cit.tum.de December 2, 2024 13:02 — with GitHub Actions Inactive
Copy link

@zagemello zagemello left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works as expected, tested on TS2

Copy link
Contributor

@SimonEntholzer SimonEntholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code

@krusche krusche added this to the 7.7.4 milestone Dec 3, 2024
@krusche krusche changed the title Communication: Add auto-list prefixing without selecting list options Communication: Fix an issue with list formatting in Markdown Dec 3, 2024
@krusche krusche merged commit 75c080a into develop Dec 3, 2024
140 of 146 checks passed
@krusche krusche deleted the feature/communication/auto-list-creation branch December 3, 2024 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix client Pull requests that update TypeScript code. (Added Automatically!) component:Communication ready to merge tests
Projects
Status: Merged
Development

Successfully merging this pull request may close these issues.

Communication: Invalid Markdown for Lists in Artemis
8 participants