main 1c2aeace65b8 cached
16 files
11.2 KB
3.4k tokens
16 symbols
1 requests
Download .txt
Repository: nadavspi/obsidian-relative-line-numbers
Branch: main
Commit: 1c2aeace65b8
Files: 16
Total size: 11.2 KB

Directory structure:
gitextract_lnic64ha/

├── .envrc
├── .github/
│   └── workflows/
│       └── release.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── README.md
├── esbuild.config.mjs
├── extension.ts
├── flake.nix
├── main.ts
├── manifest.json
├── package.json
├── styles.css
├── tsconfig.json
├── version-bump.mjs
└── versions.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .envrc
================================================
use flake


================================================
FILE: .github/workflows/release.yml
================================================
name: Release Obsidian plugin

on:
  push:
    tags:
      - "*"

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "18.x"

      - name: Build plugin
        run: |
          npm install
          npm run build

      - name: Create release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          tag="${GITHUB_REF#refs/tags/}"

          gh release create "$tag" \
            --title="$tag" \
            --draft \
            main.js manifest.json styles.css


================================================
FILE: .gitignore
================================================
# Intellij
*.iml
.idea

# npm
node_modules
package-lock.json

# build
main.js
*.js.map

================================================
FILE: .prettierignore
================================================
# build
main.js
*.js.map

================================================
FILE: .prettierrc.json
================================================
{
    "semi": true
}


================================================
FILE: README.md
================================================
## Obsidian Relative Line Numbers Plugin

![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nadavspi/obsidian-relative-line-numbers?style=for-the-badge)
![GitHub all releases](https://img.shields.io/github/downloads/nadavspi/obsidian-relative-line-numbers/total?style=for-the-badge)

This [Obsidian](https://obsidian.md/) plugin enables relative line numbers (similar to Vim's relativenumber) in editor mode.

Note: the "Show line number" setting must be enabled in Editor options.

![](demo.gif)


================================================
FILE: esbuild.config.mjs
================================================
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'

const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;

const prod = (process.argv[2] === 'production');

esbuild.build({
	banner: {
		js: banner,
	},
	entryPoints: ['main.ts'],
	bundle: true,
	external: [
		'obsidian',
		'electron',
		'@codemirror/autocomplete',
		'@codemirror/closebrackets',
		'@codemirror/collab',
		'@codemirror/commands',
		'@codemirror/comment',
		'@codemirror/fold',
		'@codemirror/gutter',
		'@codemirror/highlight',
		'@codemirror/history',
		'@codemirror/language',
		'@codemirror/lint',
		'@codemirror/matchbrackets',
		'@codemirror/panel',
		'@codemirror/rangeset',
		'@codemirror/rectangular-selection',
		'@codemirror/search',
		'@codemirror/state',
		'@codemirror/stream-parser',
		'@codemirror/text',
		'@codemirror/tooltip',
		'@codemirror/view',
		...builtins],
	format: 'cjs',
	watch: !prod,
	target: 'es2016',
	logLevel: "info",
	sourcemap: prod ? false : 'inline',
	treeShaking: true,
	outfile: 'main.js',
}).catch(() => process.exit(1));


================================================
FILE: extension.ts
================================================
import { Extension } from "@codemirror/state";
import { EditorView, ViewUpdate, gutter, lineNumbers, GutterMarker } from "@codemirror/view";
import { Compartment, EditorState } from "@codemirror/state";
import {foldedRanges} from "@codemirror/language"

let relativeLineNumberGutter = new Compartment();

class Marker extends GutterMarker {
  /** The text to render in gutter */
  text: string;

  constructor(text: string) {
    super();
    this.text = text;
    this.elementClass = "relative-line-numbers-mono";
  }

  toDOM() {
    return document.createTextNode(this.text);
  }
}

function linesCharLength(state: EditorState): number {
  /**
   * Get the character length of the number of lines in the document
   * Example: 100 lines -> 3 characters
   */
  return state.doc.lines.toString().length;
}

const absoluteLineNumberGutter = gutter({
  lineMarker: (view, line) => {
    const lineNo = view.state.doc.lineAt(line.from).number;
    const charLength = linesCharLength(view.state);
    const absoluteLineNo = new Marker(lineNo.toString().padStart(charLength, " "));
    const cursorLine = view.state.doc.lineAt(
      view.state.selection.asSingle().ranges[0].to
    ).number;

    if (lineNo === cursorLine) {
      return absoluteLineNo;
    }

    return null;
  },
  initialSpacer: (view: EditorView) => {
    const spacer = new Marker("0".repeat(linesCharLength(view.state)));
    return spacer;
  },
});

function relativeLineNumbers(lineNo: number, state: EditorState) {
  const charLength = linesCharLength(state);
  const blank = " ".padStart(charLength, " ");
  if (lineNo > state.doc.lines) {
    return blank;
  }
  const cursorLine = state.doc.lineAt(
    state.selection.asSingle().ranges[0].to
  ).number;
  

  const start = Math.min( state.doc.line(lineNo).from, 
                          state.selection.asSingle().ranges[0].to)

  const stop = Math.max( state.doc.line(lineNo).from, 
                          state.selection.asSingle().ranges[0].to)

  const folds = foldedRanges(state)
  let foldedCount = 0
  folds.between(start, stop, (from, to) => {
    let rangeStart = state.doc.lineAt(from).number
    let rangeStop = state.doc.lineAt(to).number
    foldedCount += rangeStop - rangeStart
  })

  if (lineNo === cursorLine) {
    return blank;
  } else {
    return (Math.abs(cursorLine - lineNo) - foldedCount).toString().padStart(charLength, " ");
  }
}
// This shows the numbers in the gutter
const showLineNumbers = relativeLineNumberGutter.of(
  lineNumbers({ formatNumber: relativeLineNumbers })
);

// This ensures the numbers update
// when selection (cursorActivity) happens
const lineNumbersUpdateListener = EditorView.updateListener.of(
  (viewUpdate: ViewUpdate) => {
    if (viewUpdate.selectionSet) {
      viewUpdate.view.dispatch({
        effects: relativeLineNumberGutter.reconfigure(
          lineNumbers({ formatNumber: relativeLineNumbers })
        ),
      });
    }
  }
);

export function lineNumbersRelative(): Extension {
  return [absoluteLineNumberGutter, showLineNumbers, lineNumbersUpdateListener];
}


================================================
FILE: flake.nix
================================================
{

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  outputs = { self, nixpkgs }:
    let
      overlays = [];
      supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
      forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f {
        pkgs = import nixpkgs { inherit overlays system; };
      });
    in
    {
      devShells = forEachSupportedSystem ({ pkgs }: {
        default = pkgs.mkShell {
          packages = with pkgs; [ nodejs nodePackages.typescript ];
        };
      });
    };
}


================================================
FILE: main.ts
================================================
import { Plugin } from "obsidian";
import { lineNumbersRelative } from "./extension";
import { Extension } from "@codemirror/state";

export default class RelativeLineNumbers extends Plugin {
  private editorExtension: Extension[] = [];
  enabled: boolean;

  isLegacy() {
    return (this.app as any).vault.config?.legacyEditor;
  }

  async onload() {
    this.registerEditorExtension(this.editorExtension);
    // @ts-ignore
    const showLineNumber: Boolean = this.app.vault.getConfig("showLineNumber");
    if (showLineNumber) {
      this.enable();
    }

    this.setupConfigChangeListener();
    this.addCommand({
      id: "toggle-relative-line-numbers",
      name: "Toggle Relative Line Numbers",
      callback: () => {
        if (showLineNumber) {
          if (this.enabled) {
            this.disable();
          } else {
            this.enable();
          }
        }
      },
    });
  }

  onunload() {
    this.disable();
  }

  enable() {
    this.enabled = true;

    if (this.isLegacy()) {
      this.legacyEnable();
    } else {
      this.editorExtension.length = 0;
      this.editorExtension.push(lineNumbersRelative());
      this.app.workspace.updateOptions();
    }
  }

  disable() {
    this.enabled = false;
    if (this.isLegacy()) {
      this.legacyDisable();
    } else {
      this.editorExtension.length = 0;
      this.app.workspace.updateOptions();
    }
  }

  legacyEnable() {
    this.registerCodeMirror((cm) => {
      cm.on("cursorActivity", this.legacyRelativeLineNumbers);
    });
  }

  legacyDisable() {
    this.app.workspace.iterateCodeMirrors((cm) => {
      cm.off("cursorActivity", this.legacyRelativeLineNumbers);
      cm.setOption(
        "lineNumberFormatter",
        // @ts-ignore
        CodeMirror.defaults["lineNumberFormatter"]
      );
    });
  }

  setupConfigChangeListener() {
    // @ts-ignore
    const configChangedEvent = this.app.vault.on("config-changed", () => {
      const showLineNumber: Boolean =
        // @ts-ignore
        this.app.vault.getConfig("showLineNumber");
      if (showLineNumber && !this.enabled) {
        this.enable();
      } else if (!showLineNumber && this.enabled) {
        this.disable();
      }
    });

    // @ts-ignore
    configChangedEvent.ctx = this;

    this.registerEvent(configChangedEvent);
  }

  legacyRelativeLineNumbers(cm: CodeMirror.Editor) {
    const current = cm.getCursor().line + 1;
    if (cm.state.curLineNum === current) {
      return;
    }
    cm.state.curLineNum = current;
    cm.setOption("lineNumberFormatter", (line: number) => {
      if (line === current) {
        return String(current);
      }

      return String(Math.abs(current - line));
    });
  }
}


================================================
FILE: manifest.json
================================================
{
	"id": "obsidian-relative-line-numbers",
	"name": "Relative Line Numbers",
	"version": "3.0.0",
	"minAppVersion": "1.4.16",
	"description": "Enables relative line numbers in editor mode",
	"author": "Nadav Spiegelman",
	"authorUrl": "https://nadav.is",
	"isDesktopOnly": false
}

================================================
FILE: package.json
================================================
{
  "name": "obsidian-relative-line-numbers",
  "version": "3.0.0",
  "description": "Enables relative line numbers in editor mode",
  "main": "main.js",
  "scripts": {
    "dev": "node esbuild.config.mjs",
    "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
    "version": "node version-bump.mjs && git add manifest.json versions.json"
  },
  "keywords": [],
  "author": "Nadav Spiegelman",
  "license": "MIT",
  "devDependencies": {
    "@codemirror/language": "^6.6.0",
    "@codemirror/state": "^6.2.0",
    "@codemirror/view": "^6.2.0",
    "@types/node": "^16.11.6",
    "@typescript-eslint/eslint-plugin": "^5.2.0",
    "@typescript-eslint/parser": "^5.2.0",
    "builtin-modules": "^3.2.0",
    "esbuild": "0.13.12",
    "obsidian": "1.4.11",
    "tslib": "2.3.1",
    "typescript": "^5.3.3"
  }
}


================================================
FILE: styles.css
================================================
.relative-line-numbers-mono {
  font-family: monospace;
  white-space: pre;
}

.cm-lineNumbers {
  font-family: monospace;
  white-space: pre;
  min-width: 25px; /* prevent relative line numbers from shifting on files with ~10-20 lines */
}

================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "baseUrl": ".",
    "inlineSourceMap": true,
    "inlineSources": true,
    "module": "ESNext",
    "target": "es5",
    "allowJs": true,
    "noImplicitAny": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "lib": [
      "dom",
      "es5",
      "scripthost",
      "es2015"
    ]
  },
  "include": [
    "**/*.ts"
  ]
}


================================================
FILE: version-bump.mjs
================================================
import { readFileSync, writeFileSync } from "fs";

const targetVersion = process.env.npm_package_version;

// read minAppVersion from manifest.json and bump version to target version
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));

// update versions.json with target version and minAppVersion from manifest.json
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));


================================================
FILE: versions.json
================================================
{
	"1.0.3": "0.10.0",
	"1.0.2": "0.10.0",
	"1.0.1": "0.10.0",
	"2.0.0": "0.13.14",
	"2.0.1": "0.13.14",
	"3.0.0": "1.4.16"
}
Download .txt
gitextract_lnic64ha/

├── .envrc
├── .github/
│   └── workflows/
│       └── release.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── README.md
├── esbuild.config.mjs
├── extension.ts
├── flake.nix
├── main.ts
├── manifest.json
├── package.json
├── styles.css
├── tsconfig.json
├── version-bump.mjs
└── versions.json
Download .txt
SYMBOL INDEX (16 symbols across 2 files)

FILE: extension.ts
  class Marker (line 8) | class Marker extends GutterMarker {
    method constructor (line 12) | constructor(text: string) {
    method toDOM (line 18) | toDOM() {
  function linesCharLength (line 23) | function linesCharLength(state: EditorState): number {
  function relativeLineNumbers (line 52) | function relativeLineNumbers(lineNo: number, state: EditorState) {
  function lineNumbersRelative (line 102) | function lineNumbersRelative(): Extension {

FILE: main.ts
  class RelativeLineNumbers (line 5) | class RelativeLineNumbers extends Plugin {
    method isLegacy (line 9) | isLegacy() {
    method onload (line 13) | async onload() {
    method onunload (line 37) | onunload() {
    method enable (line 41) | enable() {
    method disable (line 53) | disable() {
    method legacyEnable (line 63) | legacyEnable() {
    method legacyDisable (line 69) | legacyDisable() {
    method setupConfigChangeListener (line 80) | setupConfigChangeListener() {
    method legacyRelativeLineNumbers (line 99) | legacyRelativeLineNumbers(cm: CodeMirror.Editor) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
  {
    "path": ".envrc",
    "chars": 10,
    "preview": "use flake\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 636,
    "preview": "name: Release Obsidian plugin\n\non:\n  push:\n    tags:\n      - \"*\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n"
  },
  {
    "path": ".gitignore",
    "chars": 96,
    "preview": "# Intellij\r\n*.iml\r\n.idea\r\n\r\n# npm\r\nnode_modules\r\npackage-lock.json\r\n\r\n# build\r\nmain.js\r\n*.js.map"
  },
  {
    "path": ".prettierignore",
    "chars": 26,
    "preview": "# build\r\nmain.js\r\n*.js.map"
  },
  {
    "path": ".prettierrc.json",
    "chars": 21,
    "preview": "{\n    \"semi\": true\n}\n"
  },
  {
    "path": "README.md",
    "chars": 531,
    "preview": "## Obsidian Relative Line Numbers Plugin\r\n\r\n![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/na"
  },
  {
    "path": "esbuild.config.mjs",
    "chars": 1176,
    "preview": "import esbuild from \"esbuild\";\nimport process from \"process\";\nimport builtins from 'builtin-modules'\n\nconst banner =\n`/*"
  },
  {
    "path": "extension.ts",
    "chars": 3073,
    "preview": "import { Extension } from \"@codemirror/state\";\nimport { EditorView, ViewUpdate, gutter, lineNumbers, GutterMarker } from"
  },
  {
    "path": "flake.nix",
    "chars": 588,
    "preview": "{\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n  };\n\n  outputs = { self, nixpkgs }:\n    let\n"
  },
  {
    "path": "main.ts",
    "chars": 2821,
    "preview": "import { Plugin } from \"obsidian\";\r\nimport { lineNumbersRelative } from \"./extension\";\r\nimport { Extension } from \"@code"
  },
  {
    "path": "manifest.json",
    "chars": 280,
    "preview": "{\n\t\"id\": \"obsidian-relative-line-numbers\",\n\t\"name\": \"Relative Line Numbers\",\n\t\"version\": \"3.0.0\",\n\t\"minAppVersion\": \"1.4"
  },
  {
    "path": "package.json",
    "chars": 834,
    "preview": "{\n  \"name\": \"obsidian-relative-line-numbers\",\n  \"version\": \"3.0.0\",\n  \"description\": \"Enables relative line numbers in e"
  },
  {
    "path": "styles.css",
    "chars": 240,
    "preview": ".relative-line-numbers-mono {\n  font-family: monospace;\n  white-space: pre;\n}\n\n.cm-lineNumbers {\n  font-family: monospac"
  },
  {
    "path": "tsconfig.json",
    "chars": 395,
    "preview": "{\r\n  \"compilerOptions\": {\r\n    \"baseUrl\": \".\",\r\n    \"inlineSourceMap\": true,\r\n    \"inlineSources\": true,\r\n    \"module\": "
  },
  {
    "path": "version-bump.mjs",
    "chars": 648,
    "preview": "import { readFileSync, writeFileSync } from \"fs\";\n\nconst targetVersion = process.env.npm_package_version;\n\n// read minAp"
  },
  {
    "path": "versions.json",
    "chars": 124,
    "preview": "{\n\t\"1.0.3\": \"0.10.0\",\n\t\"1.0.2\": \"0.10.0\",\n\t\"1.0.1\": \"0.10.0\",\n\t\"2.0.0\": \"0.13.14\",\n\t\"2.0.1\": \"0.13.14\",\n\t\"3.0.0\": \"1.4.1"
  }
]

About this extraction

This page contains the full source code of the nadavspi/obsidian-relative-line-numbers GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (11.2 KB), approximately 3.4k tokens, and a symbol index with 16 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!