DEV Community

Cover image for How to query specific line(s) from a file using GitHub APIs
Tamimi
Tamimi

Posted on

How to query specific line(s) from a file using GitHub APIs

Permalinks

One of the neat features about GitHub is the generation of permalinks urls that highlights a range of line numbers. This is particularly useful when you want to highlight a chunk of code or a line to show it to someone else.

To do so, just navigate to the file you want on github and click on one line, hit the Shift key and click on the end line for the code block. This generates a URL with the highlighted block content that you can then use for sharing.

Edit: feedback based on comment
Clicking on the start line number will show the ellipses button ... hold down shift and click on the end line number. Then click on the overflow button and select Copy permalink

Tip:
You can just add #L{start_line}-L{end_line} to the URL as follows
https://github.com/{org_name}/{repo_name}/{path_to_file}#L{start_line}-L{end_line}

Note: You can perhaps do the same thing with other version control platforms like bitbucket or gitlabs but I have not checked.

Show me the API!

I was recently working on a project that dynamically generates code snippet from files hosted on github. Naturally, I referred to GitHub's APIs to query file content. Simple right? Well let's dig deeper into it.

Navigating through Github's APIs, I was disappointed to find out there is no API that query a specific line number(s) from a file. The Search API only allows to query the full content of the file. No problem, let's dig into coding logic for post processing.

Show me the code!

To bypass this limitation, I was able to fetch the content of the file using the SearchAPI as follows

async function fetchContent(orgName, repoName, file, ref) {
  const baseURL = `https://api.github.com/repos/${orgName}/${repoName}/contents/${file}?ref=${ref}`
  let res = await fetch(baseURL).catch(err => {
    throw new Error(`Error fetching content from ${baseURL}. ${err}`)
  })

  if (!res.ok){
    throw new Error(`Response status from ${baseURL}: ${res.status}`)
  }

  let body = await res.text()
  // Content body from github is base64 encoded
  return Base64.decode(JSON.parse(body).content)
}

let content = await fetchContent(orgName, repoName, file, ref)
Enter fullscreen mode Exit fullscreen mode

This brings in the whole file as text with new line delimiters presented as \n. You see where I'm going with this πŸ‘€

From here, you can split() the text content using \n as the separator and store it in an array

content = content.split('\n')
Enter fullscreen mode Exit fullscreen mode

And then simply generate the snippet using a getSlice(content,range) method

let snippet = getSlice(content,range)
Enter fullscreen mode Exit fullscreen mode

Where getSlice is defined as follows

// Splits the content given the range a-b or just line number a
const getSlice = (content, range) => {
  if (range.includes('-')){
    var a = range.split("-")[0] - 1
    var b = range.split("-")[1]
    return content.slice(a,b).join('\r\n')
  } else if (parseInt(range)) {
    return content[parseInt(range)-1]
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: range is passed as a string with a - delimiter between the start_line and end_line

And thats it!

What about you?

Did you find this useful? I am curious to know if anyone has another approach for this since GitHub does not have an API to do so (yet!)

Oldest comments (2)

Collapse
 
myleftshoe profile image
myleftshoe

Nice tip! Although the description to get the permalink was not immediately clear until I looked at the gif animation. To clarify: you have to click on the start line number (which will show the overflow button ...) then hold down shift and click on the end line number. Then click on the overflow button and select `Copy permalink".

Initially I was clicking on the start and end of the text which does not show the overflow button.

Collapse
 
tweettamimi profile image
Tamimi

Thanks for this feedback @myleftshoe ! I will update the content with this πŸ‘