provide support for 2024.3+#19
Merged
leewyatt merged 23 commits intoleewyatt:masterfrom Apr 23, 2026
Merged
Conversation
Owner
|
@tuhin47 Thank you so much for updating the project to support the latest IDEA version! I really appreciate your contribution. I'll find some time to test it soon. 😊 |
- Groovy build.gradle / settings.gradle -> Kotlin DSL (.kts) - org.jetbrains.intellij 1.17.4 -> org.jetbrains.intellij.platform 2.11.0 - Gradle wrapper 8.8 -> 8.14 (plugin v2 requires 8.13+) - Pin Gradle daemon to JBR 21.0.9 (system default JDK 25 is unparseable) - commons-dbcp 1.4 -> commons-dbcp2 2.12.0 (BasicDataSource.setMaxActive -> setMaxTotal) - commons-pool 1.6 dropped (dbcp2 bundles pool2) - commons-dbutils 1.7 -> 1.8.1 - sqlite-jdbc 3.34.0 -> 3.46.1.0 - junit 5.6.0 -> 5.10.2 - since-build 222 -> 233 via patchPluginXml, until-build left empty - pluginVersion 1.40 -> 1.41 via gradle.properties - runIde: add -Dsun.java2d.metal=false for macOS UI-freeze mitigation Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every catch site now uses LOG.warn("<context>", e) with a context-
specific message describing the operation being attempted. Previously
exceptions went silently to stderr, making user bug reports hard to
diagnose.
LOG.warn (not LOG.error) — these are expected failure modes (IO, SQL,
JSON parse) where a "Report to JetBrains" dialog would be wrong.
Exceptions preserved:
- Comment-only printStackTrace references left alone
- ImportUtil.java:164 uses printStackTrace(PrintWriter) to format the
trace into a user-facing import-error dialog; that's a legitimate
stack-trace-to-string conversion, not a silent log drop
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
IntelliJ 2024.1+ new UI renders ToolWindow strip icons in a 20x20 slot. Without this variant the platform upscales the 13x13 base SVG, which looks fuzzy on the strip. Branded gradient colors preserved — they're intentional (this isn't a monochrome system icon). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two user-facing assurances that the plugin's data is never trapped:
1. DatabaseBackupService
- Runs in DatabaseBasicService constructor before initTable() / ALTER
- Compares AppSettingsState.lastKnownPluginVersion to the live descriptor
version; copies notebooks.db to ~/.ideaNotebooksFile/backups/ when they
differ, then updates the setting
- Keeps the last 5 backups, prunes older ones
- Filename: notebooks_<timestamp>_from_<prev>_to_<cur>.db
2. Markdown-tree export (gear menu -> "Export as Markdown Tree")
- Each note becomes its own .md with YAML frontmatter (title, type,
source, offset_start/end, create/update times)
- Structure: <dir>/<notebook>/<chapter>/<note>.md + <notebook>/_assets/
- Image references rewritten to ../_assets/<filename>; imageDesc
preserved as HTML comment
- Filename sanitizer strips filesystem-reserved chars, caps length
- Reuses CustomFileUtil.exportImagesToDirectory for image copying
AppSettingsState.lastKnownPluginVersion is a new field (default ""), not
a rename — existing users' persisted settings load correctly and trigger
the backup once on first launch of v1.41.
Bundle keys added in both EN and zh_CN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Results of static + verifyPluginStructure/ProjectConfiguration pass: Fixed - Declared supportsKotlinPluginMode (K1 + K2) in kotlin-doc.xml, required by IDEA 2024.2.1+ when depending on org.jetbrains.kotlin - Suppressed the matching DevKit warning on the <depends> line in the main plugin.xml — the validator can't follow config-file references Intentionally left alone - since-build=233 vs target platform 242: the gap is deliberate (supports 2023.3+ IDE users). Matches JavaFX Tools reference config. - sourceCompatibility=17 vs recommended 21: the platform suggests 21 but accepts 17-compiled bytecode on its 21 runtime. JavaFX Tools keeps 17. - ChooseByNameContributor deprecated but still present in NoteFilterModel, OpenSearchBarAction, NoteChooseByname. Migration to ...ContributorEx requires untangling NoteChooseByname's inheritance from SearchRecordServiceImpl — that's L1 scope, not L0. No Compatibility problems (removed APIs) found: no getBaseDir, no old documentationProvider EP, no PathManager.getOptionsFile, StartupActivity already migrated to ProjectActivity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- <version> bumped in plugin.xml (also injected by patchPluginXml from gradle.properties on build) - <change-notes> rewritten for 1.41: auto-backup, Markdown tree export, build-chain modernization. HTML formatted, bilingual, old entries kept - Actions ActivateNotebookToolWindow and OpenNotebookSearchBarAction no longer carry hardcoded text="..." attributes — resolved from bundle via action.<id>.text / action.<id>.description, matching the project convention used by EditorAddNoteAction and EditorInsertCodeAction - Added matching bundle keys in EN and zh_CN - Suppressed PluginXmlValidity on every <applicationService> / <projectService> with a comment explaining why @service migration is deferred (NoteChooseByname extends SearchRecordServiceImpl blocks the required `final`) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LICENSE is the canonical Apache 2.0 text fetched from apache.org. Chose Apache 2.0 over MIT because it adds explicit patent grant + patent retaliation (any contributor suing over patents loses their license), plus more thorough warranty/liability disclaimers. Same "AS IS" protection as MIT at the bottom line, with stronger defense for the author against future contributor disputes. README.md (English) and README.zh-CN.md (Chinese) cover the same ground: what the plugin is, features, install, data locations, shortcuts, export options, build instructions, project layout, and the license/contact blurb. Cross-linked at the top of each file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ntime
Symptom: clicking through the Search dialog (or any first-time
BasicDataSource instantiation) crashes with
LinkageError: loader constraint violation: when resolving method
'org.slf4j.ILoggerFactory StaticLoggerBinder.getLoggerFactory()'
... different Class objects for the type org/slf4j/ILoggerFactory
... at BasicDataSource.<clinit>(BasicDataSource.java:71)
Root cause: dbcp2 2.10.0 (Jan 2024) bumped its transitive
commons-logging from 1.2 to 1.3.x. The new commons-logging includes
a Slf4jLogFactory that auto-bridges to SLF4J at runtime. Inside the
IntelliJ plugin classloader hierarchy, the platform's bundled SLF4J
and the one resolved through our plugin's dependency graph end up
as two different Class objects → LinkageError on first BasicDataSource
instantiation.
Fix: downgrade dbcp2 from 2.12.0 to 2.9.0 (the last release still
depending on commons-logging 1.2, which uses java.util.logging and
never touches SLF4J). Verified via `./gradlew dependencies`:
commons-dbcp2:2.9.0
+--- commons-pool2:2.10.0
\--- commons-logging:1.2
No source changes required: setMaxTotal / setDriverClassName APIs
are identical between 2.9 and 2.12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same class-of-bug as the dbcp2 downgrade, different library:
Startup crash:
PluginException: Cannot init toolwindow NoteWindowFactory
Caused by: LinkageError: loader constraint violation
at org.slf4j.LoggerFactory.getILoggerFactory(...)
at org.sqlite.JDBC.<clinit>(JDBC.java:26) <-- here
at o.a.c.dbcp2.DriverFactory.createDriver(...)
sqlite-jdbc 3.39.3.0 (Sept 2022) swapped java.util.logging for SLF4J.
Inside the IntelliJ plugin classloader hierarchy, the bundled slf4j-api
(pulled in through sqlite-jdbc) and the StaticLoggerBinder loaded by
another plugin's classloader end up with different org.slf4j.ILoggerFactory
Class objects -> LinkageError. Zero collision on 3.39.2.0 (last JUL
release) and earlier.
3.39.2.0 still gives us:
- Apple Silicon (aarch64) native binaries (added in 3.36)
- SQLite engine 3.39.2 (2022, modern enough)
- Security/stability fixes over the original 3.34.0
Verified via ./gradlew dependencies --configuration runtimeClasspath:
no slf4j-api anywhere on the runtime classpath. Plugin starts cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NotebookDaoImpl.delete and ChapterDaoImpl.delete ran 2-3 separate DELETE statements with no transaction. If any but the last statement failed (disk full, DB lock, crash), the database was left with orphan rows: chapters whose parent notebook was gone, or notes whose chapter was gone. UI would show stale children that can't be re-selected. Wrap both methods in setAutoCommit(false) + commit/rollback. On any SQL failure, rollback and log; autoCommit is always restored in finally. Implementation detail: bypass BaseDAO.update(conn, sql) in the transactional path — that helper swallows SQLException internally (existing project convention) which would defeat rollback. Use queryRunner.update(conn, sql) directly so the catch block can actually observe failures. The existing service-layer try/catch pattern is preserved — delete() still never throws. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two static fields exposed by NoteChooseByname were public mutable
ArrayLists shared across all projects and threads:
public static List<NoteNavigationItem> list = new ArrayList<>();
public static List<SearchRecord> records;
Every NoteChooseByname construction ran list.clear() then list.add()
in a loop with no synchronization. The IDE's Go To Symbol path invokes
getNames()/getItemsByName() from both EDT and background threads, and
opening a second project constructs another instance — concurrent
reads against an in-progress clear/rebuild could return partial or
inconsistent results, or ConcurrentModificationException.
Fix: store immutable snapshots in volatile fields. Constructor builds
a fresh List.copyOf(...) and assigns atomically — readers always see
either the old or new snapshot, never a partial state.
API surface change:
- Fields made private. Added public static getRecords() accessor.
- Two external readers updated to use the getter:
- NoteItemPresentation.getLocationString
- OpenSearchBarAction.actionPerformed
- Fixed a pre-existing latent NPE in both readers: .findFirst().get()
would throw if the cache was empty (possible on fresh IDE start
before any search). Now .orElse(null) + null-guard.
Default value changed from null to List.of() so pre-init access
returns empty list rather than NPEing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (P1-1) 12 inline 'color:red' / 'color:blue' spans across three *TableCellEditor files were hard-coded, so the conflict-warning balloons showed pure red and pure blue regardless of IDE theme. Dark mode especially: the blue was nearly invisible on the dark balloon background. - PluginColors: added HTML_EMPHASIS_COLOR JBColor + two static helpers warnHtmlColor() and emphasisHtmlColor(). Each helper runs ColorUtil.toHex on the JBColor at call time, so the resolved hex tracks the current theme (switching light/dark picks up on the next notification). - NotebookTableCellEditor, ChapterTableCellEditor, NoteTableCellEditor: swap the 12 inline color literals for the helper calls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ImageRecord.imagePath is read from user-editable JSON during import and
from DB rows during export. The previous code trusted it:
PluginConstant.IMAGE_DIRECTORY_PATH.resolve(imageRecord.getImagePath())
A JSON payload carrying "../../etc/passwd" (or any "../") would escape
the plugin's data directory. Could read/overwrite files outside
~/.ideaNotebooksFile/ during export, or during copy-to-export-dir
could escape the user-chosen export root.
Fix: new private resolveInside(Path base, String relative) that calls
.resolve().normalize() and verifies the result startsWith(base). Returns
null on rejection so callers skip the entry. LOG.warn records the
rejected path for visibility.
Call sites updated:
- exportImagesToDirectory: both source (IMAGE_DIRECTORY_PATH) and
destination (export dir) are now resolved through resolveInside.
- deleteImagesAndThumb: protects against a rogue image name reaching
the filesystem via the delete path.
Import-time directory copy (ImportUtil.java:45-48) is NOT changed —
it uses File.getName() which is already basename-only, and the parent
path is chosen by the user through the FileChooser, so not attacker-
controlled in the same way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every time the user clicked an image row the ListSelectionListener ran ImageIcon icon = new ImageIcon(thumbFile.getAbsolutePath()); on the EDT. That constructor reads the file synchronously, and nothing was cached — arrow-keying through a long list reloaded every thumbnail from disk on each hop. Added THUMB_CACHE: a synchronized access-ordered LinkedHashMap capped at 200 entries. Key is absolutePath + "?" + lastModified, so the cache invalidates itself whenever a thumbnail on disk is replaced (no explicit invalidation hook needed when the user updates or deletes an image). Worst-case memory ≈ 200 × ~40KB (JPEG thumb) ≈ 8MB; typical working set far smaller. Cache is static to survive panel re-creation across note switches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Typing "java" into the note type field fired the DocumentListener four times: 4 DB updates, 4 onNoteUpdated broadcasts to the message bus, 4 RecordListener handlers run across every open project. Multi-project sessions were noticeably chatty. Added an Alarm.ThreadToUse.SWING_THREAD scheduler disposed with the project. Each keystroke cancels any pending request and schedules a new one 500ms out. Only the last request runs — one DB write, one broadcast per word typed. The parallel focusLost listener is unchanged: on blur we still save whatever the field currently holds (even if the debounced write didn't fire yet, because 500ms hasn't elapsed). That path is idempotent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All three Task.Backgroundable ctors in ExportUtil passed `false` for cancellable, so a slow export (large DB, many images) gave the user no way out short of killing the IDE. - exportJsonAndImage: false -> true, checkCanceled() before JSON writing - exportMarkdownFile: false -> true, checkCanceled() before image copy - exportMarkdownTree: false -> true, checkCanceled() at top of each notebook and chapter loop iteration Export is side-effect-idempotent (partial outputs can be removed by the user), so cancellation leaves no inconsistent state in the plugin's own DB. ImportUtil was already cancellable and is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ProjectActivity is a Kotlin suspend-fun interface; when implemented from Java the suspend machinery reappears as the Continuation parameter. A synchronous Java implementation must return Unit.INSTANCE to signal "done", or COROUTINE_SUSPENDED for async work. The previous code returned the continuation parameter itself, which is neither of those — the platform's tolerant coroutine bridge accepted it in practice (the plugin functioned), but it's formally wrong and could break on any tightening of the coroutine protocol. No behavior change expected on current platforms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P2-6 (ExportUtil.processToJsonString(int)): drop the Collections.singletonList + for-loop dance that iterated exactly once. Guard against findById returning null (previously would have NPE'd on the implicit unboxing of getId()). P2-7 (ImportUtil.addNotesFromJson): remove the redundant first loop that set notebookId/chapterId on every note — the second loop sets the same fields again while also populating the array. The first loop was a no-op. P2-9 (DatabaseBasicService.isColumnExists): guard against queryRunner.query returning null (rare, but possible on corrupted DB). Previously would have NPE'd in the for-each, during first-run schema migration. P2-10 (table cell editors): hoist setClickCountToStart(200) into PluginConstant.TABLE_EDIT_CLICK_COUNT_START. Three identical magic numbers became one named constant. P2-12 (CustomUIUtil.writeImageToFile): delete the dead FileOutputStream that was allocated but never used — ImageIO.write(destImage, ext, destFile) opens its own stream internally. The now-unused java.io.FileOutputStream and java.io.OutputStream imports are removed. P2-13 (AppSettingsState.readOnlyMode): mark volatile. Import runs on a background task and writes this field while action update() handlers read it from BGT/EDT; volatile gives the needed happens-before. P2-17 (CustomFileUtil.exportImagesToDirectory): count and LOG.warn missing images so a "my export has broken links" bug report can be diagnosed from idea.log without adding a user-facing balloon. P2-18 (ImportUtil): surface an "empty file, nothing imported" notification instead of silently returning — users were confusing empty-file imports with successful ones. Bundle keys added in both en and zh_CN. P2-20 (MainPanel): the three 0.5f splitter ratios are now one named constant DEFAULT_SPLITTER_RATIO. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… desc font Three long-standing GitHub issues that share the same area (ToolWindow layout + description panel) fixed together: GitHub leewyatt#8 — "default width" (2022-12) Resizing a column, closing the IDE, and reopening used to reset the column to its hard-coded default. Now persisted per-project. - ProjectStorage: three new float fields contentPaneProportion / leftPaneProportion / rightPaneProportion. - MainPanel: construct splitters with the default JBSplitter(false), then apply the saved values in the constructor. Register a PropertyChangeListener on "proportion" for each splitter — every drag writes back to ProjectStorage. No explicit "save" hook needed. - Listeners registered AFTER initial setProportion, so the programmatic startup calls don't echo-write themselves. GitHub leewyatt#7 — "界面四块区域的布局比例" (2022-08) The original 0.5/0.5/0.5 equal split gave notebook/chapter/note/detail a 25/25/25/25 width share. Users kept manually widening the detail pane, but that width was lost on restart (see leewyatt#8 above). New defaults: contentPane = 0.3 (notebook+chapter : note+detail) leftPane = 0.5 (notebook : chapter) rightPane = 0.2 (note : detail) Roughly a 15/15/14/56 split — content gets majority of the width. Users can drag to taste; it will be saved. GitHub leewyatt#6 — "font in description is a little bigger than other" (2022-04) fieldDesc used AppSettingsState.customFontName/Size — defaults MONOSPACED / 18pt. That made the description TextArea visibly larger than the labels / combos / type field around it. Smart default in DetailPanel.resolveDescFont: if the user's saved font values equal the original plugin defaults (MONOSPACED, 18), they almost certainly never actively picked them → use JBFont.regular() (IDE UI font). If they changed a value via Settings → Tools → Notebook, respect that choice. The existing onSetCustomFont message-bus listener still applies updates from the settings dialog. Compatibility notes: - ProjectStorage XML from v1.41 won't have the new proportion fields; XmlSerializer simply leaves the Java defaults (new content-biased ratios) in place on first load. No migration needed. - Users who had customFontSize persisted at the plugin-default 18 will see a smaller description font after upgrade. This is the fix they asked for in leewyatt#6. Users who intentionally set a custom size see their choice preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The old "显示列表" row exposed four DumbAwareToggleAction icons — one
per panel. Each had its own isSelected() tied to its own panel's
visibility, so in the "full 4-column" mode three of them appeared
selected at the same time. Users interpreted them as independent
toggles (checkbox semantics) when they were actually radio-style mode
switches, and clicking any one rewrote visibility for all three panels.
Replaced with a single ComboBox of LayoutMode { FULL, CHAPTER_PLUS,
NOTE_PLUS, CONTENT_ONLY }. Each mode carries its own icon, bundle key,
and the intended visibility of notebook/chapter/note panels. The combo
renders icon + localized label (e.g. "Full (4 columns)" /
"完整 (4 列)"). Radio semantics now match the widget.
ProjectStorage changes:
- Added layoutMode field, default FULL. This is the source of truth
going forward.
- Legacy notebookPaneVisible/chapterPaneVisible/notePaneVisible fields
retained so old XML still deserializes.
- loadState migrates old XML: if layoutMode is absent but the legacy
booleans are set to a non-trivial combination, LayoutMode is
reconstructed via fromLegacyVisibility(...). All-false (plugin's
original Java default) is treated as "no explicit preference" and
upgrades to the new FULL default — new users and un-customized
users now see the full 4-column layout on first open.
- getState syncs the legacy booleans back from layoutMode on every
save, so downgrading to plugin 1.41 still produces correct
visibility from XML.
DetailPanel changes:
- Added buildLayoutComboBox() + applyLayoutMode().
- Removed initNotebookVisibleAction / initChapterVisibleAction /
initNoteVisibleAction / initDetailVisibleAction (230 lines of
toggle-action boilerplate).
- Removed getVisiblePanelToolbar().
- computeWidth() no longer force-resets splitter proportions on
every mode switch. That reset predated the per-drag persistence
introduced for GitHub leewyatt#8 and was undoing the user's adjustments.
- controlViewVisible() no longer writes the three legacy booleans
directly; ProjectStorage.getState() derives them from layoutMode.
MainPanel.resetPanesVisible() reads from layoutMode instead of the
three booleans.
Bundle: removed 4 mainPanel.action.showXxx.text keys (no longer
referenced from code); added mainPanel.layout.label and four
mainPanel.layout.* keys in both en and zh_CN.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two small but linked changes per plugin-author feedback: 1. Default LayoutMode changed from FULL to CONTENT_ONLY. On a fresh install the DB is empty, so three empty notebook/chapter/note lists are just clutter. The detail panel alone is the cleanest starting point; users open the Layout combo-box to reveal the hierarchy once they have data to browse. This also matches the pre-1.42 implicit behaviour (all visibility booleans defaulted to false), which the previous 1.42 default of FULL had silently changed. 2. loadState migration rewritten to not assume a specific default. Previously: "if layoutMode == FULL after copyBean, treat as un-migrated and rebuild from legacy booleans." That check was brittle — changing the Java default would silently break it. New: rebuild layoutMode from legacy booleans whenever the two disagree. post-1.42 saves keep them in sync via getState, so a disagreement only happens on upgrade from pre-1.42 XML (where the layoutMode field is absent and falls back to the current Java default). This approach is robust to any future default change. Traced all eight combinations of (pre/post 1.42, four user-intent modes) through the load path and each produces the expected final layoutMode. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
leewyatt
added a commit
that referenced
this pull request
Apr 23, 2026
…place Description rewrite: - Opening line highlights the plugin's actual value prop — code-aware, local-first, no cloud / account / telemetry — rather than "This is a note plugin." The 2021 description read like a feature manifest and missed the "why would I install this in 2026" question. - Features regrouped by user scenario (Capture / Organize / Export & Backup / Configure) instead of by UI surface (dialog / button / menu item). Easier to scan on the Marketplace detail page. - Removed the "Compact View / Full View" bullets — obsolete terms, replaced in v1.41 by the layout-mode ComboBox (Full / Chapter+ / Note+ / Detail only). - Mentioned the v1.41 additions that users will actually care about: Markdown-tree export, auto-backup on upgrade, splitter-width persistence. - @tuhin47 added to the thanks list. - Bilingual structure preserved; en and zh_CN blocks separated by <hr/> for clarity. - Updated CSS to style <h4> as thematic group headers. Change-notes rewrite: - Scoped to the v1.41 release only. Removed the <h3>1.40</h3> section (never published to Marketplace; users jumping from 1.38/1.39 → 1.41 don't need notes for an intermediate they never saw). - Restructured into five themes: Compatibility, Data safety, UX, Performance, Under the hood. Previous version was 5 mixed bullets. - @tuhin47 credited for PR #19 (initial 2024.3 compat fix). - Links to GitHub issues #6, #7, #8 where relevant so curious users can trace the change to the original report. - Both en and zh_CN versions mirror the same structure. XML validated via ElementTree.parse — no structural issues. verifyPluginStructure reports only the known cosmetic supportsKotlinPluginMode warning (validator limitation — declaration is in kotlin-doc.xml which the verifier doesn't follow). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.